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!: allow to import created dids (and remove legacy publicDidSeed) #1325

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 0 additions & 22 deletions demo/src/BaseAgent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import {
import { AnonCredsRsModule } from '@aries-framework/anoncreds-rs'
import { AskarModule } from '@aries-framework/askar'
import {
TypedArrayEncoder,
KeyType,
DidsModule,
V2ProofProtocol,
V2CredentialProtocol,
Expand Down Expand Up @@ -53,7 +51,6 @@ export class BaseAgent {
public name: string
public config: InitConfig
public agent: DemoAgent
public anonCredsIssuerId: string
public useLegacyIndySdk: boolean

public constructor({
Expand All @@ -74,15 +71,12 @@ export class BaseAgent {
id: name,
key: name,
},
publicDidSeed: 'afjdemoverysercure00000000000000',
endpoints: [`http://localhost:${this.port}`],
autoAcceptConnections: true,
} satisfies InitConfig

this.config = config

// TODO: do not hardcode this
this.anonCredsIssuerId = '2jEvRuKmfBJTRa7QowDpNN'
this.useLegacyIndySdk = useLegacyIndySdk

this.agent = new Agent({
Expand All @@ -97,22 +91,6 @@ export class BaseAgent {
public async initializeAgent() {
await this.agent.initialize()

// FIXME:
// We need to make sure the key to submit transactions is created. We should update this to use the dids module, and allow
// to add an existing did based on a seed/secretKey, and not register it on the the ledger. However for Indy SDK we currently
// use the deprecated publicDidSeed property (which will register the did in the wallet), and for Askar we manually create the key
// in the wallet.
if (!this.useLegacyIndySdk) {
try {
await this.agent.context.wallet.createKey({
keyType: KeyType.Ed25519,
privateKey: TypedArrayEncoder.fromString('afjdemoverysercure00000000000000'),
})
} catch (error) {
// We assume the key already exists, and that's why askar failed
}
}

console.log(greenText(`\nAgent ${this.name} created!\n`))
}
}
Expand Down
32 changes: 29 additions & 3 deletions demo/src/Faber.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { RegisterCredentialDefinitionReturnStateFinished } from '../../pack
import type { ConnectionRecord, ConnectionStateChangedEvent } from '@aries-framework/core'
import type BottomBar from 'inquirer/lib/ui/bottom-bar'

import { utils, ConnectionEventTypes } from '@aries-framework/core'
import { KeyType, TypedArrayEncoder, utils, ConnectionEventTypes } from '@aries-framework/core'
import { ui } from 'inquirer'

import { BaseAgent } from './BaseAgent'
Expand All @@ -11,16 +11,35 @@ import { Color, greenText, Output, purpleText, redText } from './OutputClass'
export class Faber extends BaseAgent {
public outOfBandId?: string
public credentialDefinition?: RegisterCredentialDefinitionReturnStateFinished
public anonCredsIssuerId?: string
public ui: BottomBar

public constructor(port: number, name: string) {
super({ port, name })
super({ port, name, useLegacyIndySdk: true })
this.ui = new ui.BottomBar()
}

public static async build(): Promise<Faber> {
const faber = new Faber(9001, 'faber')
await faber.initializeAgent()

// NOTE: we assume the did is already registered on the ledger, we just store the private key in the wallet
// and store the existing did in the wallet
const privateKey = TypedArrayEncoder.fromString('afjdemoverysercure00000000000000')

const key = await faber.agent.wallet.createKey({
keyType: KeyType.Ed25519,
privateKey,
})

// did is first 16 bytes of public key encoded as base58
const unqualifiedIndyDid = TypedArrayEncoder.toBase58(key.publicKey.slice(0, 16))
await faber.agent.dids.import({
did: `did:sov:${unqualifiedIndyDid}`,
})

faber.anonCredsIssuerId = unqualifiedIndyDid

return faber
}

Expand Down Expand Up @@ -102,6 +121,9 @@ export class Faber extends BaseAgent {
}

private async registerSchema() {
if (!this.anonCredsIssuerId) {
throw new Error(redText('Missing anoncreds issuerId'))
}
const schemaTemplate = {
name: 'Faber College' + utils.uuid(),
version: '1.0.0',
Expand All @@ -120,14 +142,18 @@ export class Faber extends BaseAgent {

if (schemaState.state !== 'finished') {
throw new Error(
`Error registering schema: ${schemaState.state === 'failed' ? schemaState.reason : 'Not Finished'}}`
`Error registering schema: ${schemaState.state === 'failed' ? schemaState.reason : 'Not Finished'}`
)
}
this.ui.updateBottomBar('\nSchema registered!\n')
return schemaState
}

private async registerCredentialDefinition(schemaId: string) {
if (!this.anonCredsIssuerId) {
throw new Error(redText('Missing anoncreds issuerId'))
}

this.ui.updateBottomBar('\nRegistering credential definition...\n')
const { credentialDefinitionState } = await this.agent.modules.anoncreds.registerCredentialDefinition({
credentialDefinition: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,6 @@ describe('V1 Proofs - Connectionless - Indy', () => {

const { credentialDefinition } = await prepareForAnonCredsIssuance(faberAgent, {
attributeNames: ['name', 'age', 'image_0', 'image_1'],
issuerId: faberAgent.publicDid?.did as string,
})

const [faberConnection, aliceConnection] = await makeConnection(faberAgent, aliceAgent)
Expand Down
15 changes: 9 additions & 6 deletions packages/anoncreds/tests/anoncreds.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Agent, KeyDerivationMethod } from '@aries-framework/core'
import { Agent, KeyDerivationMethod, KeyType, TypedArrayEncoder } from '@aries-framework/core'
import { agentDependencies } from '@aries-framework/node'
import indySdk from 'indy-sdk'
import * as indySdk from 'indy-sdk'

import { IndySdkModule } from '../../indy-sdk/src/IndySdkModule'
import { AnonCredsCredentialDefinitionRepository, AnonCredsModule, AnonCredsSchemaRepository } from '../src'
Expand Down Expand Up @@ -186,10 +186,13 @@ describe('AnonCreds API', () => {
})

test('register a credential definition', async () => {
// NOTE: the indy-sdk MUST have a did created, we can't just create a key
await agent.context.wallet.initPublicDid({ seed: '00000000000000000000000000000My1' })
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const issuerId = agent.context.wallet.publicDid!.did
// Create key
await agent.wallet.createKey({
privateKey: TypedArrayEncoder.fromString('00000000000000000000000000000My1'),
keyType: KeyType.Ed25519,
})

const issuerId = 'VsKV7grR1BUE29mG2Fm2kX'

const credentialDefinitionResult = await agent.modules.anoncreds.registerCredentialDefinition({
credentialDefinition: {
Expand Down
21 changes: 14 additions & 7 deletions packages/anoncreds/tests/legacyAnonCredsSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {
import type { AutoAcceptProof, ConnectionRecord } from '@aries-framework/core'

import {
TypedArrayEncoder,
CacheModule,
InMemoryLruCache,
Agent,
Expand All @@ -30,11 +31,14 @@ import { randomUUID } from 'crypto'

import { AnonCredsRsModule } from '../../anoncreds-rs/src'
import { AskarModule } from '../../askar/src'
import { sleep } from '../../core/src/utils/sleep'
import { uuid } from '../../core/src/utils/uuid'
import { setupSubjectTransports, setupEventReplaySubjects } from '../../core/tests'
import {
getAgentOptions,
importExistingIndyDidFromPrivateKey,
makeConnection,
publicDidSeed,
genesisTransactions,
taaVersion,
taaAcceptanceMechanism,
Expand Down Expand Up @@ -403,9 +407,6 @@ export async function setupAnonCredsTests<

const { credentialDefinition, schema } = await prepareForAnonCredsIssuance(issuerAgent, {
attributeNames,
// TODO: replace with more dynamic / generic value We should create a did using the dids module
// and use that probably
issuerId: issuerAgent.publicDid?.did as string,
})

let issuerHolderConnection: ConnectionRecord | undefined
Expand Down Expand Up @@ -441,10 +442,10 @@ export async function setupAnonCredsTests<
} as unknown as SetupAnonCredsTestsReturn<VerifierName, CreateConnections>
}

export async function prepareForAnonCredsIssuance(
agent: Agent,
{ attributeNames, issuerId }: { attributeNames: string[]; issuerId: string }
) {
export async function prepareForAnonCredsIssuance(agent: Agent, { attributeNames }: { attributeNames: string[] }) {
// Add existing endorser did to the wallet
const issuerId = await importExistingIndyDidFromPrivateKey(agent, TypedArrayEncoder.fromString(publicDidSeed))

const schema = await registerSchema(agent, {
// TODO: update attrNames to attributeNames
attrNames: attributeNames,
Expand All @@ -453,12 +454,18 @@ export async function prepareForAnonCredsIssuance(
issuerId,
})

// Wait some time pass to let ledger settle the object
await sleep(1000)

const credentialDefinition = await registerCredentialDefinition(agent, {
schemaId: schema.schemaId,
issuerId,
tag: 'default',
})

// Wait some time pass to let ledger settle the object
await sleep(1000)

return {
schema,
credentialDefinition,
Expand Down
12 changes: 6 additions & 6 deletions packages/askar/src/storage/AskarStorageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from '@aries-framework/core'
import { Scan } from '@hyperledger/aries-askar-shared'

import { askarErrors, isAskarError } from '../utils/askarError'
import { AskarErrorCode, isAskarError } from '../utils/askarError'
import { assertAskarWallet } from '../utils/assertAskarWallet'

import { askarQueryFromSearchQuery, recordToInstance, transformFromRecordTagValues } from './utils'
Expand All @@ -29,7 +29,7 @@ export class AskarStorageService<T extends BaseRecord> implements StorageService
try {
await session.insert({ category: record.type, name: record.id, value, tags })
} catch (error) {
if (isAskarError(error) && error.code === askarErrors.Duplicate) {
if (isAskarError(error, AskarErrorCode.Duplicate)) {
throw new RecordDuplicateError(`Record with id ${record.id} already exists`, { recordType: record.type })
}

Expand All @@ -50,7 +50,7 @@ export class AskarStorageService<T extends BaseRecord> implements StorageService
try {
await session.replace({ category: record.type, name: record.id, value, tags })
} catch (error) {
if (isAskarError(error) && error.code === askarErrors.NotFound) {
if (isAskarError(error, AskarErrorCode.NotFound)) {
throw new RecordNotFoundError(`record with id ${record.id} not found.`, {
recordType: record.type,
cause: error,
Expand All @@ -69,7 +69,7 @@ export class AskarStorageService<T extends BaseRecord> implements StorageService
try {
await session.remove({ category: record.type, name: record.id })
} catch (error) {
if (isAskarError(error) && error.code === askarErrors.NotFound) {
if (isAskarError(error, AskarErrorCode.NotFound)) {
throw new RecordNotFoundError(`record with id ${record.id} not found.`, {
recordType: record.type,
cause: error,
Expand All @@ -91,7 +91,7 @@ export class AskarStorageService<T extends BaseRecord> implements StorageService
try {
await session.remove({ category: recordClass.type, name: id })
} catch (error) {
if (isAskarError(error) && error.code === askarErrors.NotFound) {
if (isAskarError(error, AskarErrorCode.NotFound)) {
throw new RecordNotFoundError(`record with id ${id} not found.`, {
recordType: recordClass.type,
cause: error,
Expand All @@ -117,7 +117,7 @@ export class AskarStorageService<T extends BaseRecord> implements StorageService
} catch (error) {
if (
isAskarError(error) &&
(error.code === askarErrors.NotFound ||
(error.code === AskarErrorCode.NotFound ||
// FIXME: this is current output from askar wrapper but does not describe specifically a not found scenario
error.message === 'Received null pointer. The native library could not find the value.')
) {
Expand Down
5 changes: 3 additions & 2 deletions packages/askar/src/utils/askarError.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AriesAskarError } from '@hyperledger/aries-askar-shared'

export enum askarErrors {
export enum AskarErrorCode {
Success = 0,
Backend = 1,
Busy = 2,
Expand All @@ -13,4 +13,5 @@ export enum askarErrors {
Custom = 100,
}

export const isAskarError = (error: Error) => error instanceof AriesAskarError
export const isAskarError = (error: Error, askarErrorCode?: AskarErrorCode): error is AriesAskarError =>
error instanceof AriesAskarError && (askarErrorCode === undefined || error.code === askarErrorCode)
Loading