Skip to content

Commit

Permalink
feat: add indynamespace for ledger id for anoncreds (#965)
Browse files Browse the repository at this point in the history
Signed-off-by: Moriarty <[email protected]>
  • Loading branch information
morrieinmaas authored Oct 7, 2022
1 parent 5e9e0fc commit df3777e
Show file tree
Hide file tree
Showing 24 changed files with 269 additions and 79 deletions.
4 changes: 4 additions & 0 deletions .github/actions/setup-postgres-wallet-plugin/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ author: '[email protected]'
runs:
using: composite
steps:
# cargo build failing on latest release of rust due to
# socket2 dependency in the plugin https://users.rust-lang.org/t/build-broken-with-parse-quote-spanned-is-ambiguous/80280/2
# 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
curl https://sh.rustup.rs -sSf | bash -s -- -y
export PATH="/root/.cargo/bin:${PATH}"
rustup default 1.63.0
cd ../
git clone https://github.com/hyperledger/indy-sdk.git
cd indy-sdk/experimental/plugins/postgres_storage/
Expand Down
3 changes: 3 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ RUN apt-get install -y --no-install-recommends yarn
RUN curl https://sh.rustup.rs -sSf | bash -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"

# cargo build failing on latest release of rust due to socket2 dependency in the plugin https://users.rust-lang.org/t/build-broken-with-parse-quote-spanned-is-ambiguous/80280/2 so pointing rust version to 1.63.0
RUN rustup default 1.63.0

# clone indy-sdk and build postgres plugin
RUN git clone https://github.com/hyperledger/indy-sdk.git
WORKDIR /indy-sdk/experimental/plugins/postgres_storage/
Expand Down
1 change: 1 addition & 0 deletions demo/src/BaseAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export class BaseAgent {
{
genesisTransactions: bcovrin,
id: 'greenlights' + name,
indyNamespace: 'greenlights' + name,
isProduction: false,
},
],
Expand Down
2 changes: 2 additions & 0 deletions docs/getting-started/ledger.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ const agentConfig: InitConfig = {
indyLedgers: [
{
id: 'sovrin-main',
didIndyNamespace: 'sovrin',
isProduction: true,
genesisPath: './genesis/sovrin-main.txn',
},
{
id: 'bcovrin-test',
didIndyNamespace: 'bcovrin:test',
isProduction: false,
genesisTransactions: 'XXXX',
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ export class IndyCredentialFormatService extends CredentialFormatService<IndyCre
credentialDefinition,
}
)

credentialRecord.metadata.set(CredentialMetadataKeys.IndyRequest, credentialRequestMetadata)
credentialRecord.metadata.set(CredentialMetadataKeys.IndyCredential, {
credentialDefinitionId: credentialOffer.cred_def_id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const agentOptions = getAgentOptions('Faber Dids Registrar', {
id: `localhost`,
isProduction: false,
genesisPath,
indyNamespace: 'localhost',
transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' },
},
],
Expand Down Expand Up @@ -200,7 +201,7 @@ describe('dids', () => {
qualifiedIndyDid: `did:indy:localhost:${indyDid}`,
},
didRegistrationMetadata: {
indyNamespace: 'localhost',
didIndyNamespace: 'localhost',
},
didState: {
state: 'finished',
Expand Down
7 changes: 3 additions & 4 deletions packages/core/src/modules/dids/methods/sov/SovDidRegistrar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,8 @@ export class SovDidRegistrar implements DidRegistrar {
// Build did document.
const didDocument = didDocumentBuilder.build()

// FIXME: we need to update this to the `indyNamespace` once https://github.com/hyperledger/aries-framework-javascript/issues/944 has been resolved
const indyNamespace = this.indyPoolService.ledgerWritePool.config.id
const qualifiedIndyDid = `did:indy:${indyNamespace}:${unqualifiedIndyDid}`
const didIndyNamespace = this.indyPoolService.ledgerWritePool.config.indyNamespace
const qualifiedIndyDid = `did:indy:${didIndyNamespace}:${unqualifiedIndyDid}`

// Save the did so we know we created it and can issue with it
const didRecord = new DidRecord({
Expand All @@ -122,7 +121,7 @@ export class SovDidRegistrar implements DidRegistrar {
qualifiedIndyDid,
},
didRegistrationMetadata: {
indyNamespace,
didIndyNamespace,
},
didState: {
state: 'finished',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const IndyLedgerServiceMock = IndyLedgerService as jest.Mock<IndyLedgerService>
jest.mock('../../../../ledger/services/IndyPoolService')
const IndyPoolServiceMock = IndyPoolService as jest.Mock<IndyPoolService>
const indyPoolServiceMock = new IndyPoolServiceMock()
mockProperty(indyPoolServiceMock, 'ledgerWritePool', { config: { id: 'pool1' } } as IndyPool)
mockProperty(indyPoolServiceMock, 'ledgerWritePool', { config: { id: 'pool1', indyNamespace: 'pool1' } } as IndyPool)

const agentConfig = getAgentConfig('SovDidRegistrar')

Expand Down Expand Up @@ -148,7 +148,7 @@ describe('DidRegistrar', () => {
qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ',
},
didRegistrationMetadata: {
indyNamespace: 'pool1',
didIndyNamespace: 'pool1',
},
didState: {
state: 'finished',
Expand Down Expand Up @@ -222,7 +222,7 @@ describe('DidRegistrar', () => {
qualifiedIndyDid: 'did:indy:pool1:R1xKJw17sUoXhejEpugMYJ',
},
didRegistrationMetadata: {
indyNamespace: 'pool1',
didIndyNamespace: 'pool1',
},
didState: {
state: 'finished',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,23 @@
import type { CredDef } from 'indy-sdk'

import { BaseRecord } from '../../../storage/BaseRecord'
import { didFromCredentialDefinitionId } from '../../../utils/did'
import { uuid } from '../../../utils/uuid'

export interface AnonCredsCredentialDefinitionRecordProps {
credentialDefinition: CredDef
}

export type DefaultAnonCredsCredentialDefinitionTags = {
credentialDefinitionId: string
issuerDid: string
schemaId: string
tag: string
}

export class AnonCredsCredentialDefinitionRecord extends BaseRecord<DefaultAnonCredsCredentialDefinitionTags> {
export class AnonCredsCredentialDefinitionRecord extends BaseRecord {
public static readonly type = 'AnonCredsCredentialDefinitionRecord'
public readonly type = AnonCredsCredentialDefinitionRecord.type

public readonly credentialDefinition!: CredDef

public constructor(props: AnonCredsCredentialDefinitionRecordProps) {
super()

if (props) {
this.id = uuid()
this.credentialDefinition = props.credentialDefinition
}
}
Expand All @@ -31,9 +26,6 @@ export class AnonCredsCredentialDefinitionRecord extends BaseRecord<DefaultAnonC
return {
...this._tags,
credentialDefinitionId: this.credentialDefinition.id,
issuerDid: didFromCredentialDefinitionId(this.credentialDefinition.id),
schemaId: this.credentialDefinition.schemaId,
tag: this.credentialDefinition.tag,
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import type { Schema } from 'indy-sdk'

import { BaseRecord } from '../../../storage/BaseRecord'
import { didFromSchemaId } from '../../../utils/did'
import { uuid } from '../../../utils/uuid'

export interface AnonCredsSchemaRecordProps {
schema: Schema
id?: string
}

export type DefaultAnonCredsSchemaTags = {
Expand All @@ -17,12 +19,14 @@ export type DefaultAnonCredsSchemaTags = {
export class AnonCredsSchemaRecord extends BaseRecord<DefaultAnonCredsSchemaTags> {
public static readonly type = 'AnonCredsSchemaRecord'
public readonly type = AnonCredsSchemaRecord.type

public readonly schema!: Schema

public constructor(props: AnonCredsSchemaRecordProps) {
super()

if (props) {
this.id = props.id ?? uuid()
this.schema = props.schema
}
}
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/modules/ledger/IndyPool.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { AgentDependencies } from '../../agent/AgentDependencies'
import type { Logger } from '../../logger'
import type { FileSystem } from '../../storage/FileSystem'
import type { DidIndyNamespace } from '../../utils/indyIdentifiers'
import type * as Indy from 'indy-sdk'
import type { Subject } from 'rxjs'

Expand All @@ -20,6 +21,7 @@ export interface IndyPoolConfig {
genesisTransactions?: string
id: string
isProduction: boolean
indyNamespace: DidIndyNamespace
transactionAuthorAgreement?: TransactionAuthorAgreement
}

Expand Down Expand Up @@ -52,6 +54,10 @@ export class IndyPool {
})
}

public get didIndyNamespace(): string {
return this.didIndyNamespace
}

public get id() {
return this.poolConfig.id
}
Expand Down
67 changes: 56 additions & 11 deletions packages/core/src/modules/ledger/LedgerApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,18 @@ import { AriesFrameworkError } from '../../error'
import { IndySdkError } from '../../error/IndySdkError'
import { injectable } from '../../plugins'
import { isIndyError } from '../../utils/indyError'
import {
getLegacyCredentialDefinitionId,
getLegacySchemaId,
getQualifiedIndyCredentialDefinitionId,
getQualifiedIndySchemaId,
} from '../../utils/indyIdentifiers'
import { AnonCredsCredentialDefinitionRecord } from '../indy/repository/AnonCredsCredentialDefinitionRecord'
import { AnonCredsCredentialDefinitionRepository } from '../indy/repository/AnonCredsCredentialDefinitionRepository'
import { AnonCredsSchemaRecord } from '../indy/repository/AnonCredsSchemaRecord'
import { AnonCredsSchemaRepository } from '../indy/repository/AnonCredsSchemaRepository'

import { LedgerModuleConfig } from './LedgerModuleConfig'
import { generateCredentialDefinitionId, generateSchemaId } from './ledgerUtil'
import { IndyLedgerService } from './services'

@injectable()
Expand Down Expand Up @@ -73,16 +80,33 @@ export class LedgerApi {
throw new AriesFrameworkError('Agent has no public DID.')
}

const schemaId = generateSchemaId(did, schema.name, schema.version)
const schemaId = getLegacySchemaId(did, schema.name, schema.version)

// Generate the qualified ID
const qualifiedIdentifier = getQualifiedIndySchemaId(this.ledgerService.getDidIndyWriteNamespace(), schemaId)

// Try find the schema in the wallet
const schemaRecord = await this.anonCredsSchemaRepository.findBySchemaId(this.agentContext, schemaId)
// Schema in wallet
if (schemaRecord) return schemaRecord.schema
const schemaRecord = await this.anonCredsSchemaRepository.findById(this.agentContext, qualifiedIdentifier)
// Schema in wallet
if (schemaRecord) {
// Transform qualified to unqualified
return {
...schemaRecord.schema,
id: schemaId,
}
}

const schemaFromLedger = await this.findBySchemaIdOnLedger(schemaId)

if (schemaFromLedger) return schemaFromLedger
return this.ledgerService.registerSchema(this.agentContext, did, schema)
const createdSchema = await this.ledgerService.registerSchema(this.agentContext, did, schema)

const anonCredsSchema = new AnonCredsSchemaRecord({
schema: { ...createdSchema, id: qualifiedIdentifier },
})
await this.anonCredsSchemaRepository.save(this.agentContext, anonCredsSchema)

return createdSchema
}

private async findBySchemaIdOnLedger(schemaId: string) {
Expand Down Expand Up @@ -115,18 +139,32 @@ export class LedgerApi {
}

// Construct credential definition ID
const credentialDefinitionId = generateCredentialDefinitionId(
const credentialDefinitionId = getLegacyCredentialDefinitionId(
did,
credentialDefinitionTemplate.schema.seqNo,
credentialDefinitionTemplate.tag
)

// Construct qualified identifier
const qualifiedIdentifier = getQualifiedIndyCredentialDefinitionId(
this.ledgerService.getDidIndyWriteNamespace(),
credentialDefinitionId
)

// Check if the credential exists in wallet. If so, return it
const credentialDefinitionRecord = await this.anonCredsCredentialDefinitionRepository.findByCredentialDefinitionId(
const credentialDefinitionRecord = await this.anonCredsCredentialDefinitionRepository.findById(
this.agentContext,
credentialDefinitionId
qualifiedIdentifier
)
if (credentialDefinitionRecord) return credentialDefinitionRecord.credentialDefinition

// Credential Definition in wallet
if (credentialDefinitionRecord) {
// Transform qualified to unqualified
return {
...credentialDefinitionRecord.credentialDefinition,
id: credentialDefinitionId,
}
}

// Check for the credential on the ledger.
const credentialDefinitionOnLedger = await this.findByCredentialDefinitionIdOnLedger(credentialDefinitionId)
Expand All @@ -137,10 +175,17 @@ export class LedgerApi {
}

// Register the credential
return await this.ledgerService.registerCredentialDefinition(this.agentContext, did, {
const registeredDefinition = await this.ledgerService.registerCredentialDefinition(this.agentContext, did, {
...credentialDefinitionTemplate,
signatureType: 'CL',
})
// Replace the unqualified with qualified Identifier in anonCred
const anonCredCredential = new AnonCredsCredentialDefinitionRecord({
credentialDefinition: { ...registeredDefinition, id: qualifiedIdentifier },
})
await this.anonCredsCredentialDefinitionRepository.save(this.agentContext, anonCredCredential)

return registeredDefinition
}

public async getCredentialDefinition(id: string) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ const CacheRepositoryMock = CacheRepository as jest.Mock<CacheRepository>

const pools: IndyPoolConfig[] = [
{
id: 'sovrinMain',
id: 'sovrin',
indyNamespace: 'sovrin',
isProduction: true,
genesisTransactions: 'xxx',
transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,30 +24,35 @@ const CacheRepositoryMock = CacheRepository as jest.Mock<CacheRepository>
const pools: IndyPoolConfig[] = [
{
id: 'sovrinMain',
indyNamespace: 'sovrin',
isProduction: true,
genesisTransactions: 'xxx',
transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' },
},
{
id: 'sovrinBuilder',
indyNamespace: 'sovrin:builder',
isProduction: false,
genesisTransactions: 'xxx',
transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' },
},
{
id: 'sovrinStaging',
id: 'sovringStaging',
indyNamespace: 'sovrin:staging',
isProduction: false,
genesisTransactions: 'xxx',
transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' },
},
{
id: 'indicioMain',
indyNamespace: 'indicio',
isProduction: true,
genesisTransactions: 'xxx',
transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' },
},
{
id: 'bcovrinTest',
indyNamespace: 'bcovrin:test',
isProduction: false,
genesisTransactions: 'xxx',
transactionAuthorAgreement: { version: '1', acceptanceMechanism: 'accept' },
Expand Down Expand Up @@ -280,6 +285,7 @@ describe('IndyPoolService', () => {
const { pool } = await poolService.getPoolForDid(agentContext, did)

expect(pool.config.id).toBe('sovrinBuilder')
expect(pool.config.indyNamespace).toBe('sovrin:builder')

const cacheRecord = spy.mock.calls[0][1]
expect(cacheRecord.entries.length).toBe(1)
Expand Down
Loading

0 comments on commit df3777e

Please sign in to comment.