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 @@
+
+
+
+
+Aries Framework JavaScript AnonCreds RS Module
+
+
+
+
+
+
+
+
+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==