Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(indy-vdr): add IndyVdrAnonCredsRegistry #1270

Merged
merged 13 commits into from
Feb 10, 2023
1 change: 1 addition & 0 deletions packages/indy-vdr/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
},
"dependencies": {
"@aries-framework/core": "0.3.3",
"@aries-framework/anoncreds": "0.3.3",
"@hyperledger/indy-vdr-shared": "^0.1.0-dev.4"
},
"devDependencies": {
Expand Down
375 changes: 375 additions & 0 deletions packages/indy-vdr/src/anoncreds/indyVdrAnoncredsRegistry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,375 @@
import type {
AnonCredsRegistry,
GetCredentialDefinitionReturn,
GetSchemaReturn,
RegisterSchemaOptions,
RegisterCredentialDefinitionOptions,
RegisterSchemaReturn,
RegisterCredentialDefinitionReturn,
GetRevocationListReturn,
GetRevocationRegistryDefinitionReturn
} from '@aries-framework/anoncreds'
import { AgentContext, DidsApi, getKeyDidMappingByVerificationMethod, AriesFrameworkError } from '@aries-framework/core'
import { CredentialDefinitionRequest, GetSchemaRequest, GetCredentialDefinitionRequest, SchemaRequest } 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<GetSchemaReturn> {
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',
message: `unable to resolve credential definition: ${error.message}`,
Vickysomtee marked this conversation as resolved.
Show resolved Hide resolved
},
schemaMetadata: {},
}
}
}

public async registerSchema(
agentContext: AgentContext,
options: IndyVdrRegisterSchemaOptions
): Promise<RegisterSchemaReturn> {
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
// 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) throw new AriesFrameworkError('Unable to resolve did')

const verificationMethod = didResult.didDocument.dereferenceKey('did:sov:${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<GetCredentialDefinitionReturn> {
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<RegisterCredentialDefinitionReturn> {
// 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);

//TODO: get schema SeqNo
const credentialDefinitionId = getLegacyCredentialDefinitionId(options.options.didIndyNamespace, options.credentialDefinition., options.credentialDefinition.tag),

const credentialDefinitionRequest = new CredentialDefinitionRequest({
submitterDid: options.credentialDefinition.issuerId,
credentialDefinition: {
ver: '1.0',
id: credentialDefinitionId,
schemaId: options.credentialDefinition.schemaId,
type: 'CL',
tag: options.credentialDefinition.tag,
value: {
primary: options.credentialDefinition.value,

},
},
})

const pool = indyVdrPoolService.getPoolForNamespace(options.options.didIndyNamespace);

const didsApi = agentContext.dependencyManager.resolve(DidsApi)
const didResult = await didsApi.resolve(`did:sov:${options.credentialDefinition.issuerId}`)

if (!didResult.didDocument) throw new AriesFrameworkError('Unable to resolve did')
Vickysomtee marked this conversation as resolved.
Show resolved Hide resolved

const verificationMethod = didResult.didDocument.dereferenceKey('did:sov:${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 getRevocationList(agentContext: AgentContext, revocationRegistryId: string, timestamp: number): Promise<GetRevocationListReturn> {
return {
resolutionMetadata: {
error: 'Not Implemented',
message: `Revocation list not yet implemented `,
},
revocationListMetadata: {},
}
}

public async getRevocationRegistryDefinition(
agentContext: AgentContext,
revocationRegistryDefinitionId: string
): Promise<GetRevocationRegistryDefinitionReturn> {
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
}
}

Loading