Skip to content

Commit

Permalink
fix: cheqd create from did document (#1850)
Browse files Browse the repository at this point in the history
Signed-off-by: Timo Glastra <timo@animo.id>
  • Loading branch information
TimoGlastra authored May 1, 2024
1 parent e9238cf commit dcd028e
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 10 deletions.
5 changes: 3 additions & 2 deletions packages/cheqd/src/anoncreds/utils/identifiers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { CheqdNetwork } from '@cheqd/sdk'
import type { ParsedDid } from '@credo-ts/core'

import { TypedArrayEncoder, utils } from '@credo-ts/core'
Expand Down Expand Up @@ -28,7 +29,7 @@ export const cheqdResourceMetadataRegex = new RegExp(
`^did:cheqd:${NETWORK}:${IDENTIFIER}/resources/${IDENTIFIER}/metadata${QUERY}${FRAGMENT}`
)

export type ParsedCheqdDid = ParsedDid & { network: string }
export type ParsedCheqdDid = ParsedDid & { network: `${CheqdNetwork}` }
export function parseCheqdDid(didUrl: string): ParsedCheqdDid | null {
if (didUrl === '' || !didUrl) return null
const sections = didUrl.match(cheqdSdkAnonCredsRegistryIdentifierRegex)
Expand All @@ -44,7 +45,7 @@ export function parseCheqdDid(didUrl: string): ParsedCheqdDid | null {
const parts: ParsedCheqdDid = {
did: `did:cheqd:${sections[1]}:${sections[2]}`,
method: 'cheqd',
network: sections[1],
network: sections[1] as `${CheqdNetwork}`,
id: sections[2],
didUrl,
}
Expand Down
43 changes: 36 additions & 7 deletions packages/cheqd/src/dids/CheqdDidRegistrar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
DidCreateResult,
DidDeactivateResult,
DidUpdateResult,
DidUpdateOptions,
} from '@credo-ts/core'

import { MethodSpecificIdAlgo, createDidVerificationMethod } from '@cheqd/sdk'
Expand All @@ -26,6 +27,7 @@ import {
VerificationMethod,
} from '@credo-ts/core'

import { parseCheqdDid } from '../anoncreds/utils/identifiers'
import { CheqdLedgerService } from '../ledger'

import {
Expand All @@ -42,14 +44,28 @@ export class CheqdDidRegistrar implements DidRegistrar {
const didRepository = agentContext.dependencyManager.resolve(DidRepository)
const cheqdLedgerService = agentContext.dependencyManager.resolve(CheqdLedgerService)

const { methodSpecificIdAlgo, network, versionId = utils.uuid() } = options.options
const verificationMethod = options.secret?.verificationMethod
let didDocument: DidDocument
const versionId = options.options?.versionId ?? utils.uuid()

try {
if (options.didDocument && validateSpecCompliantPayload(options.didDocument)) {
didDocument = options.didDocument
} else if (verificationMethod) {

const cheqdDid = parseCheqdDid(options.didDocument.id)
if (!cheqdDid) {
return {
didDocumentMetadata: {},
didRegistrationMetadata: {},
didState: {
state: 'failed',
reason: `Unable to parse cheqd did ${options.didDocument.id}`,
},
}
}
} else if (options.secret?.verificationMethod) {
const withoutDidDocumentOptions = options as CheqdDidCreateWithoutDidDocumentOptions
const verificationMethod = withoutDidDocumentOptions.secret.verificationMethod
const methodSpecificIdAlgo = withoutDidDocumentOptions.options.methodSpecificIdAlgo
const privateKey = verificationMethod.privateKey
if (privateKey && !isValidPrivateKey(privateKey, KeyType.Ed25519)) {
return {
Expand All @@ -71,7 +87,7 @@ export class CheqdDidRegistrar implements DidRegistrar {
verificationMethod: verificationMethod.type as VerificationMethods,
verificationMethodId: verificationMethod.id || 'key-1',
methodSpecificIdAlgo: (methodSpecificIdAlgo as MethodSpecificIdAlgo) || MethodSpecificIdAlgo.Uuid,
network: network as CheqdNetwork,
network: withoutDidDocumentOptions.options.network as CheqdNetwork,
publicKey: TypedArrayEncoder.toHex(key.publicKey),
})

Expand Down Expand Up @@ -383,21 +399,34 @@ export class CheqdDidRegistrar implements DidRegistrar {
}
}

export interface CheqdDidCreateOptions extends DidCreateOptions {
export interface CheqdDidCreateWithoutDidDocumentOptions extends DidCreateOptions {
method: 'cheqd'
did?: undefined
didDocument?: undefined
options: {
network: `${CheqdNetwork}`
fee?: DidStdFee
versionId?: string
methodSpecificIdAlgo?: `${MethodSpecificIdAlgo}`
}
secret: {
verificationMethod?: IVerificationMethod
verificationMethod: IVerificationMethod
}
}

export interface CheqdDidUpdateOptions extends DidCreateOptions {
export interface CheqdDidCreateFromDidDocumentOptions extends DidCreateOptions {
method: 'cheqd'
did?: undefined
didDocument: DidDocument
options?: {
fee?: DidStdFee
versionId?: string
}
}

export type CheqdDidCreateOptions = CheqdDidCreateFromDidDocumentOptions | CheqdDidCreateWithoutDidDocumentOptions

export interface CheqdDidUpdateOptions extends DidUpdateOptions {
did: string
didDocument: DidDocument
options: {
Expand Down
80 changes: 79 additions & 1 deletion packages/cheqd/tests/cheqd-did-registrar.e2e.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import type { CheqdDidCreateOptions } from '../src'
import type { DidDocument } from '@credo-ts/core'

import { Agent, TypedArrayEncoder } from '@credo-ts/core'
import {
SECURITY_JWS_CONTEXT_URL,
DidDocumentBuilder,
getEd25519VerificationKey2018,
getJsonWebKey2020,
KeyType,
utils,
Agent,
TypedArrayEncoder,
} from '@credo-ts/core'
import { generateKeyPairFromSeed } from '@stablelib/ed25519'

import { getInMemoryAgentOptions } from '../../core/tests/helpers'
Expand Down Expand Up @@ -126,4 +135,73 @@ describe('Cheqd DID registrar', () => {
const resolvedDocument = await agent.dids.resolve(did)
expect(resolvedDocument.didDocumentMetadata.deactivated).toBe(true)
})

it('should create a did:cheqd did using custom did document containing Ed25519 key', async () => {
const did = `did:cheqd:testnet:${utils.uuid()}`

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

const createResult = await agent.dids.create<CheqdDidCreateOptions>({
method: 'cheqd',
didDocument: new DidDocumentBuilder(did)
.addContext(SECURITY_JWS_CONTEXT_URL)
.addVerificationMethod(
getEd25519VerificationKey2018({
key: ed25519Key,
controller: did,
id: `${did}#${ed25519Key.fingerprint}`,
})
)
.build(),
})

expect(createResult).toMatchObject({
didState: {
state: 'finished',
},
})

expect(createResult.didState.didDocument?.toJSON()).toMatchObject({
'@context': ['https://w3id.org/did/v1', 'https://w3id.org/security/suites/jws-2020/v1'],
verificationMethod: [
{
controller: did,
type: 'Ed25519VerificationKey2018',
publicKeyBase58: ed25519Key.publicKeyBase58,
},
],
})
})

it('should create a did:cheqd did using custom did document containing P256 key', async () => {
const did = `did:cheqd:testnet:${utils.uuid()}`

const p256Key = await agent.wallet.createKey({
keyType: KeyType.P256,
})

const createResult = await agent.dids.create<CheqdDidCreateOptions>({
method: 'cheqd',
didDocument: new DidDocumentBuilder(did)
.addContext(SECURITY_JWS_CONTEXT_URL)
.addVerificationMethod(
getJsonWebKey2020({
did,
key: p256Key,
verificationMethodId: `${did}#${p256Key.fingerprint}`,
})
)
.build(),
})

// FIXME: the ES256 signature generated by Credo is invalid for Cheqd
// need to dive deeper into it, but for now adding a failing test so we can fix it in the future
expect(createResult).toMatchObject({
didState: {
state: 'failed',
},
})
})
})

0 comments on commit dcd028e

Please sign in to comment.