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(dids): add did registrar #953

Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
9ad9a5f
feat: generic wallet interface for key types
TimoGlastra Mar 15, 2022
fdeef4a
feat: initial did registrar
TimoGlastra Mar 31, 2022
942c683
temp
TimoGlastra Apr 28, 2022
e7e5cbf
temp
TimoGlastra Apr 28, 2022
2ed81b1
temp
TimoGlastra Apr 28, 2022
7452fa1
merge with 0.3.0
TimoGlastra Jul 15, 2022
b1e09ac
feat: did key and did peer registrars
TimoGlastra Jul 15, 2022
f5e9e8c
feat(dids): did sov registrar
TimoGlastra Jul 16, 2022
67e5364
Merge remote-tracking branch 'upstream/0.3.0-pre' into feat/did-regis…
TimoGlastra Jul 16, 2022
448f704
Merge remote-tracking branch 'upstream/0.3.0-pre' into feat/did-regis…
TimoGlastra Jul 19, 2022
ccb3152
feat: finish dids registrar module
TimoGlastra Jul 19, 2022
20e3a4e
merge
TimoGlastra Jul 19, 2022
c6f60df
finish did module
TimoGlastra Jul 19, 2022
0852065
chore: update @types/indy-sdk
TimoGlastra Jul 19, 2022
cb2130c
Merge remote-tracking branch 'upstream/0.3.0-pre' into feat/did-regis…
TimoGlastra Jul 19, 2022
7727fc0
fix: casing of files
TimoGlastra Jul 21, 2022
f3595b8
refactor: add did prefix to services
TimoGlastra Jul 21, 2022
7e6117e
chore: address feedback
TimoGlastra Jul 21, 2022
3187ee0
style: eslint warnings
TimoGlastra Jul 21, 2022
5a7e664
Merge branch '0.3.0-pre' into feat/did-registrar-module-030-full
TimoGlastra Jul 21, 2022
5ec2f66
build: do not use unknown is catch
TimoGlastra Jul 21, 2022
79072ce
Merge branch 'feat/did-registrar-module-030-full' of github.com:TimoG…
TimoGlastra Jul 21, 2022
b32ffdc
chore: revert ts upgrade
TimoGlastra Jul 21, 2022
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
4 changes: 2 additions & 2 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"@stablelib/ed25519": "^1.0.2",
"@stablelib/random": "^1.0.1",
"@stablelib/sha256": "^1.0.1",
"@types/indy-sdk": "^1.16.19",
"@types/indy-sdk": "^1.16.21",
"@types/node-fetch": "^2.5.10",
"@types/ws": "^7.4.6",
"abort-controller": "^3.0.0",
Expand Down Expand Up @@ -63,6 +63,6 @@
"@types/varint": "^6.0.0",
"rimraf": "~3.0.2",
"tslog": "^3.2.0",
"typescript": "~4.3.0"
"typescript": "~4.6.2"
TimoGlastra marked this conversation as resolved.
Show resolved Hide resolved
}
}
55 changes: 30 additions & 25 deletions packages/core/src/modules/connections/DidExchangeProtocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { AgentContext } from '../../agent'
import type { ResolvedDidCommService } from '../../agent/MessageSender'
import type { InboundMessageContext } from '../../agent/models/InboundMessageContext'
import type { ParsedMessageType } from '../../utils/messageType'
import type { PeerDidCreateOptions } from '../dids'
import type { OutOfBandDidCommService } from '../oob/domain/OutOfBandDidCommService'
import type { OutOfBandRecord } from '../oob/repository'
import type { ConnectionRecord } from './repository'
Expand All @@ -16,14 +17,17 @@ import { Logger } from '../../logger'
import { inject, injectable } from '../../plugins'
import { JsonEncoder } from '../../utils/JsonEncoder'
import { JsonTransformer } from '../../utils/JsonTransformer'
import { DidDocument } from '../dids'
import { DidDocumentRole } from '../dids/domain/DidDocumentRole'
import { createDidDocumentFromServices } from '../dids/domain/createPeerDidFromServices'
import {
DidDocument,
DidRegistrarService,
DidDocumentRole,
createPeerDidDocumentFromServices,
DidKey,
getNumAlgoFromPeerDid,
PeerDidNumAlgo,
} from '../dids'
import { getKeyDidMappingByVerificationMethod } from '../dids/domain/key-type'
import { didKeyToInstanceOfKey } from '../dids/helpers'
import { DidKey } from '../dids/methods/key/DidKey'
import { getNumAlgoFromPeerDid, PeerDidNumAlgo } from '../dids/methods/peer/didPeer'
import { didDocumentJsonToNumAlgo1Did } from '../dids/methods/peer/peerDidNumAlgo1'
import { DidRecord, DidRepository } from '../dids/repository'
import { OutOfBandRole } from '../oob/domain/OutOfBandRole'
import { OutOfBandState } from '../oob/domain/OutOfBandState'
Expand All @@ -48,17 +52,20 @@ interface DidExchangeRequestParams {
@injectable()
export class DidExchangeProtocol {
private connectionService: ConnectionService
private didRegistrarService: DidRegistrarService
private jwsService: JwsService
private didRepository: DidRepository
private logger: Logger

public constructor(
connectionService: ConnectionService,
didRegistrarService: DidRegistrarService,
didRepository: DidRepository,
jwsService: JwsService,
@inject(InjectionSymbols.Logger) logger: Logger
) {
this.connectionService = connectionService
this.didRegistrarService = didRegistrarService
this.didRepository = didRepository
this.jwsService = jwsService
this.logger = logger
Expand Down Expand Up @@ -165,6 +172,8 @@ export class DidExchangeProtocol {
)
}

// TODO: Move this into the didcomm module, and add a method called store received did document.
// This can be called from both the did exchange and the connection protocol.
const didDocument = await this.extractDidDocument(messageContext.agentContext, message)
const didRecord = new DidRecord({
id: message.did,
Expand Down Expand Up @@ -406,32 +415,28 @@ export class DidExchangeProtocol {
}

private async createPeerDidDoc(agentContext: AgentContext, services: ResolvedDidCommService[]) {
const didDocument = createDidDocumentFromServices(services)
// Create did document without the id property
const didDocument = createPeerDidDocumentFromServices(services)

const peerDid = didDocumentJsonToNumAlgo1Did(didDocument.toJSON())
didDocument.id = peerDid

const didRecord = new DidRecord({
id: peerDid,
role: DidDocumentRole.Created,
// Register did:peer document. This will generate the id property and save it to a did record
const result = await this.didRegistrarService.create<PeerDidCreateOptions>(agentContext, {
method: 'peer',
didDocument,
tags: {
// We need to save the recipientKeys, so we can find the associated did
// of a key when we receive a message from another connection.
recipientKeyFingerprints: didDocument.recipientKeys.map((key) => key.fingerprint),
options: {
numAlgo: PeerDidNumAlgo.GenesisDoc,
},
})

this.logger.debug('Saving DID record', {
id: didRecord.id,
role: didRecord.role,
tags: didRecord.getTags(),
didDocument: 'omitted...',
if (result.didState?.state !== 'finished') {
throw new AriesFrameworkError(`Did document creation failed: ${JSON.stringify(result.didState)}`)
}

this.logger.debug(`Did document with did ${result.didState.did} created.`, {
did: result.didState.did,
didDocument: result.didState.didDocument,
})

await this.didRepository.save(agentContext, didRecord)
this.logger.debug('Did record created.', didRecord)
return didDocument
return result.didState.didDocument
}

private async createSignedAttachment(agentContext: AgentContext, didDoc: DidDocument, verkeys: string[]) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { AgentContext } from '../../../agent'
import type { Wallet } from '../../../wallet/Wallet'
import type { DidDocument } from '../../dids'
import type { Routing } from '../services/ConnectionService'

import { Subject } from 'rxjs'
Expand All @@ -22,9 +23,11 @@ import { uuid } from '../../../utils/uuid'
import { IndyWallet } from '../../../wallet/IndyWallet'
import { AckMessage, AckStatus } from '../../common'
import { DidKey, IndyAgentService } from '../../dids'
import { DidDocumentRole } from '../../dids/domain/DidDocumentRole'
import { DidCommV1Service } from '../../dids/domain/service/DidCommV1Service'
import { didDocumentJsonToNumAlgo1Did } from '../../dids/methods/peer/peerDidNumAlgo1'
import { DidRepository } from '../../dids/repository'
import { DidRecord, DidRepository } from '../../dids/repository'
import { DidRegistrarService } from '../../dids/services/DidRegistrarService'
import { OutOfBandRole } from '../../oob/domain/OutOfBandRole'
import { OutOfBandState } from '../../oob/domain/OutOfBandState'
import { ConnectionRequestMessage, ConnectionResponseMessage, TrustPingMessage } from '../messages'
Expand All @@ -43,9 +46,22 @@ import { ConnectionService } from '../services/ConnectionService'
import { convertToNewDidDocument } from '../services/helpers'

jest.mock('../repository/ConnectionRepository')
jest.mock('../../dids/services/DidRegistrarService')
jest.mock('../../dids/repository/DidRepository')
const ConnectionRepositoryMock = ConnectionRepository as jest.Mock<ConnectionRepository>
const DidRepositoryMock = DidRepository as jest.Mock<DidRepository>
const DidRegistrarServiceMock = DidRegistrarService as jest.Mock<DidRegistrarService>

const didRegistrarService = new DidRegistrarServiceMock()
mockFunction(didRegistrarService.create).mockResolvedValue({
didDocumentMetadata: {},
didRegistrationMetadata: {},
didState: {
state: 'finished',
did: 'did:peer:123',
didDocument: {} as DidDocument,
},
})

const connectionImageUrl = 'https://example.com/image.png'

Expand Down Expand Up @@ -78,13 +94,26 @@ describe('ConnectionService', () => {
eventEmitter = new EventEmitter(agentConfig.agentDependencies, new Subject())
connectionRepository = new ConnectionRepositoryMock()
didRepository = new DidRepositoryMock()
connectionService = new ConnectionService(agentConfig.logger, connectionRepository, didRepository, eventEmitter)
connectionService = new ConnectionService(
agentConfig.logger,
connectionRepository,
didRepository,
didRegistrarService,
eventEmitter
)
myRouting = {
recipientKey: Key.fromFingerprint('z6MkwFkSP4uv5PhhKJCGehtjuZedkotC7VF64xtMsxuM8R3W'),
endpoints: agentConfig.endpoints ?? [],
routingKeys: [],
mediatorId: 'fakeMediatorId',
}

mockFunction(didRepository.getById).mockResolvedValue(
new DidRecord({
id: 'did:peer:123',
role: DidDocumentRole.Created,
})
)
})

describe('createRequest', () => {
Expand Down
78 changes: 54 additions & 24 deletions packages/core/src/modules/connections/services/ConnectionService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { AgentContext } from '../../../agent'
import type { AgentMessage } from '../../../agent/AgentMessage'
import type { InboundMessageContext } from '../../../agent/models/InboundMessageContext'
import type { AckMessage } from '../../common'
import type { PeerDidCreateOptions } from '../../dids/methods/peer/PeerDidRegistrar'
import type { OutOfBandDidCommService } from '../../oob/domain/OutOfBandDidCommService'
import type { OutOfBandRecord } from '../../oob/repository'
import type { ConnectionStateChangedEvent } from '../ConnectionEvents'
Expand All @@ -21,9 +22,10 @@ import { Logger } from '../../../logger'
import { inject, injectable } from '../../../plugins'
import { JsonTransformer } from '../../../utils/JsonTransformer'
import { indyDidFromPublicKeyBase58 } from '../../../utils/did'
import { DidKey, IndyAgentService } from '../../dids'
import { DidKey, DidRegistrarService, IndyAgentService } from '../../dids'
import { DidDocumentRole } from '../../dids/domain/DidDocumentRole'
import { didKeyToVerkey } from '../../dids/helpers'
import { PeerDidNumAlgo } from '../../dids/methods/peer/didPeer'
TimoGlastra marked this conversation as resolved.
Show resolved Hide resolved
import { didDocumentJsonToNumAlgo1Did } from '../../dids/methods/peer/peerDidNumAlgo1'
import { DidRecord, DidRepository } from '../../dids/repository'
import { DidRecordMetadataKeys } from '../../dids/repository/didRecordMetadataTypes'
Expand Down Expand Up @@ -59,17 +61,20 @@ export interface ConnectionRequestParams {
export class ConnectionService {
private connectionRepository: ConnectionRepository
private didRepository: DidRepository
private didRegistrarService: DidRegistrarService
private eventEmitter: EventEmitter
private logger: Logger

public constructor(
@inject(InjectionSymbols.Logger) logger: Logger,
connectionRepository: ConnectionRepository,
didRepository: DidRepository,
didRegistrarService: DidRegistrarService,
eventEmitter: EventEmitter
) {
this.connectionRepository = connectionRepository
this.didRepository = didRepository
this.didRegistrarService = didRegistrarService
this.eventEmitter = eventEmitter
this.logger = logger
}
Expand Down Expand Up @@ -101,18 +106,15 @@ export class ConnectionService {
// We take just the first one for now.
const [invitationDid] = outOfBandInvitation.invitationDids

const { did: peerDid } = await this.createDid(agentContext, {
role: DidDocumentRole.Created,
didDoc,
})
const didDocument = await this.registerCreatedPeerDidDocument(agentContext, didDoc)

const connectionRecord = await this.createConnection(agentContext, {
protocol: HandshakeProtocol.Connections,
role: DidExchangeRole.Requester,
state: DidExchangeState.InvitationReceived,
theirLabel: outOfBandInvitation.label,
alias: config?.alias,
did: peerDid,
did: didDocument.id,
mediatorId,
autoAcceptConnection: config?.autoAcceptConnection,
outOfBandId: outOfBandRecord.id,
Expand Down Expand Up @@ -161,10 +163,7 @@ export class ConnectionService {
})
}

const { did: peerDid } = await this.createDid(messageContext.agentContext, {
role: DidDocumentRole.Received,
didDoc: message.connection.didDoc,
})
const didDocument = await this.storeReceivedPeerDidDocument(messageContext.agentContext, message.connection.didDoc)

const connectionRecord = await this.createConnection(messageContext.agentContext, {
protocol: HandshakeProtocol.Connections,
Expand All @@ -173,7 +172,7 @@ export class ConnectionService {
theirLabel: message.label,
imageUrl: message.imageUrl,
outOfBandId: outOfBandRecord.id,
theirDid: peerDid,
theirDid: didDocument.id,
threadId: message.threadId,
mediatorId: outOfBandRecord.mediatorId,
autoAcceptConnection: outOfBandRecord.autoAcceptConnection,
Expand Down Expand Up @@ -210,10 +209,7 @@ export class ConnectionService {
)
)

const { did: peerDid } = await this.createDid(agentContext, {
role: DidDocumentRole.Created,
didDoc,
})
const didDocument = await this.registerCreatedPeerDidDocument(agentContext, didDoc)

const connection = new Connection({
did: didDoc.id,
Expand All @@ -233,7 +229,7 @@ export class ConnectionService {
connectionSig: await signData(connectionJson, agentContext.wallet, signingKey),
})

connectionRecord.did = peerDid
connectionRecord.did = didDocument.id
await this.updateState(agentContext, connectionRecord, DidExchangeState.ResponseSent)

this.logger.debug(`Create message ${ConnectionResponseMessage.type.messageTypeUri} end`, {
Expand Down Expand Up @@ -309,12 +305,9 @@ export class ConnectionService {
throw new AriesFrameworkError('DID Document is missing.')
}

const { did: peerDid } = await this.createDid(messageContext.agentContext, {
role: DidDocumentRole.Received,
didDoc: connection.didDoc,
})
const didDocument = await this.storeReceivedPeerDidDocument(messageContext.agentContext, connection.didDoc)

connectionRecord.theirDid = peerDid
connectionRecord.theirDid = didDocument.id
connectionRecord.threadId = message.threadId

await this.updateState(messageContext.agentContext, connectionRecord, DidExchangeState.ResponseReceived)
Expand Down Expand Up @@ -632,15 +625,52 @@ export class ConnectionService {
return connectionRecord
}

private async createDid(agentContext: AgentContext, { role, didDoc }: { role: DidDocumentRole; didDoc: DidDoc }) {
private async registerCreatedPeerDidDocument(agentContext: AgentContext, didDoc: DidDoc) {
// Convert the legacy did doc to a new did document
const didDocument = convertToNewDidDocument(didDoc)

// Register did:peer document. This will generate the id property and save it to a did record
const result = await this.didRegistrarService.create<PeerDidCreateOptions>(agentContext, {
method: 'peer',
didDocument,
options: {
numAlgo: PeerDidNumAlgo.GenesisDoc,
},
})

if (result.didState?.state !== 'finished') {
throw new AriesFrameworkError(`Did document creation failed: ${JSON.stringify(result.didState)}`)
}

this.logger.debug(`Did document with did ${result.didState.did} created.`, {
did: result.didState.did,
didDocument: result.didState.didDocument,
})

const didRecord = await this.didRepository.getById(agentContext, result.didState.did)

// Store the unqualified did with the legacy did document in the metadata
// Can be removed at a later stage if we know for sure we don't need it anymore
didRecord.metadata.set(DidRecordMetadataKeys.LegacyDid, {
unqualifiedDid: didDoc.id,
didDocumentString: JsonTransformer.serialize(didDoc),
berendsliedrecht marked this conversation as resolved.
Show resolved Hide resolved
})

await this.didRepository.update(agentContext, didRecord)
return result.didState.didDocument
}

private async storeReceivedPeerDidDocument(agentContext: AgentContext, didDoc: DidDoc) {
// Convert the legacy did doc to a new did document
const didDocument = convertToNewDidDocument(didDoc)

// TODO: Move this into the didcomm module, and add a method called store received did document.
// This can be called from both the did exchange and the connection protocol.
const peerDid = didDocumentJsonToNumAlgo1Did(didDocument.toJSON())
didDocument.id = peerDid
const didRecord = new DidRecord({
id: peerDid,
role,
role: DidDocumentRole.Received,
didDocument,
tags: {
// We need to save the recipientKeys, so we can find the associated did
Expand All @@ -665,7 +695,7 @@ export class ConnectionService {

await this.didRepository.save(agentContext, didRecord)
this.logger.debug('Did record created.', didRecord)
return { did: peerDid, didDocument }
return didDocument
}

private createDidDoc(routing: Routing) {
Expand Down
Loading