Skip to content

Commit

Permalink
Merge pull request #17 from blu3beri/feat/w3crecord
Browse files Browse the repository at this point in the history
feat(core): w3cCredentialRecord and w3cCredentialRepository
  • Loading branch information
karimStekelenburg authored Mar 29, 2022
2 parents 7a790f6 + 001bbec commit 30474cc
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 30 deletions.
27 changes: 21 additions & 6 deletions packages/core/src/modules/vc/W3cCredentialService.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import type { JwsLinkedDataSignature } from '../../crypto/JwsLinkedDataSignature'
import type { DidInfo } from '../../wallet'
import type { VerifyCredentialResult, W3cCredential, W3cVerifyCredentialResult } from './models'
import type { VerifyPresentationResult } from './models/presentation/VerifyPresentationResult'
import type { W3cPresentation } from './models/presentation/W3Presentation'
import type { RemoteDocument, Url } from 'jsonld/jsonld-spec'

// @ts-ignore
import jsonld from '@digitalcredentials/jsonld'
import jsonld, { expand } from '@digitalcredentials/jsonld'
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import documentLoaderNode from '@digitalcredentials/jsonld/lib/documentLoaders/node'
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import documentLoaderXhr from '@digitalcredentials/jsonld/lib/documentLoaders/xhr'
import vc from '@digitalcredentials/vc'
Expand All @@ -20,12 +20,13 @@ import { Ed25519Signature2018 } from '../../crypto/Ed25519Signature2018'
import { createWalletKeyPairClass } from '../../crypto/WalletKeyPair'
import { AriesFrameworkError } from '../../error'
import { Logger } from '../../logger'
import { JsonTransformer } from '../../utils'
import { JsonTransformer, orArrayToArray } from '../../utils'
import { Wallet } from '../../wallet'
import { DidKey, DidResolverService } from '../dids'

import { W3cVerifiableCredential } from './models'
import { W3cCredentialRecord } from './models/credential/W3cCredentialRecord'
import { W3cCredentialRepository } from './models/credential/W3cCredentialRepository'
import { W3cVerifiablePresentation } from './models/presentation/W3cVerifiablePresentation'

interface LdProofDetailOptions {
Expand Down Expand Up @@ -68,6 +69,7 @@ class SignatureSuiteRegistry {
@scoped(Lifecycle.ContainerScoped)
export class W3cCredentialService {
private wallet: Wallet
private w3cCredentialRepository: W3cCredentialRepository
private didResolver: DidResolverService
private agentConfig: AgentConfig
private logger: Logger
Expand All @@ -79,11 +81,13 @@ export class W3cCredentialService {

public constructor(
@inject('Wallet') wallet: Wallet,
w3cCredentialRepository: W3cCredentialRepository,
didResolver: DidResolverService,
agentConfig: AgentConfig,
logger: Logger
) {
this.wallet = wallet
this.w3cCredentialRepository = w3cCredentialRepository
this.didResolver = didResolver
this.agentConfig = agentConfig
this.logger = logger
Expand Down Expand Up @@ -196,10 +200,21 @@ export class W3cCredentialService {
* @returns the credential record that was written to storage
*/
public async storeCredential(record: W3cVerifiableCredential): Promise<W3cCredentialRecord> {
// MOCK
return new W3cCredentialRecord({
// Get the expanded types
const expandedTypes = (await expand(JsonTransformer.toJSON(record), { documentLoader: this.documentLoader }))[0][
'@type'
]

// Create an instance of the w3cCredentialRecord
const w3cCredentialRecord = new W3cCredentialRecord({
tags: { expandedTypes: orArrayToArray(expandedTypes) },
credential: record,
})

// Store the w3c credential record
await this.w3cCredentialRepository.save(w3cCredentialRecord)

return w3cCredentialRecord
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import { DidResolverService } from '../../dids'
import { DidRepository } from '../../dids/repository'
import { IndyLedgerService } from '../../ledger/services/IndyLedgerService'
import { W3cCredentialService } from '../W3cCredentialService'
import { W3cCredential } from '../models'
import { W3cCredential, W3cVerifiableCredential } from '../models'
import { W3cCredentialRepository } from '../models/credential/W3cCredentialRepository'

const TEST_DID_KEY = 'did:key:z6Mkgg342Ycpuk263R9d8Aq6MUaxPn1DDeHyGo38EefXmgDL'

Expand All @@ -18,12 +19,16 @@ jest.mock('../../ledger/services/IndyLedgerService')
const IndyLedgerServiceMock = IndyLedgerService as jest.Mock<IndyLedgerService>
const DidRepositoryMock = DidRepository as unknown as jest.Mock<DidRepository>

jest.mock('../models/credential/W3cCredentialRepository')
const W3cCredentialRepositoryMock = W3cCredentialRepository as jest.Mock<W3cCredentialRepository>

describe('W3cCredentialService', () => {
let wallet: IndyWallet
let agentConfig: AgentConfig
let didResolverService: DidResolverService
let logger: TestLogger
let w3cCredentialService: W3cCredentialService
let w3cCredentialRepository: W3cCredentialRepository

beforeAll(async () => {
agentConfig = getAgentConfig('W3cCredentialServiceTest')
Expand All @@ -33,14 +38,65 @@ describe('W3cCredentialService', () => {
await wallet.createAndOpen(agentConfig.walletConfig!)
await wallet.initPublicDid({})
didResolverService = new DidResolverService(agentConfig, new IndyLedgerServiceMock(), new DidRepositoryMock())
w3cCredentialService = new W3cCredentialService(wallet, didResolverService, agentConfig, logger)
w3cCredentialRepository = new W3cCredentialRepositoryMock()
w3cCredentialService = new W3cCredentialService(
wallet,
w3cCredentialRepository,
didResolverService,
agentConfig,
logger
)
})

afterAll(async () => {
await wallet.delete()
})

describe('sign', () => {
describe('store', () => {
test('Store a credential', async () => {
const credential = JsonTransformer.fromJSON(
{
'@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2018/credentials/examples/v1'],
type: ['VerifiableCredential', 'UniversityDegreeCredential'],
issuer: 'did:key:z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV',
issuanceDate: '2017-10-22T12:23:48Z',
credentialSubject: {
degree: {
type: 'BachelorDegree',
name: 'Bachelor of Science and Arts',
},
},
proof: {
verificationMethod:
'did:key:z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV#z6MkvePyWAApUVeDboZhNbckaWHnqtD6pCETd6xoqGbcpEBV',
type: 'Ed25519Signature2018',
created: '2022-03-28T15:54:59Z',
proofPurpose: 'assertionMethod',
jws: 'eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..b0MD_c-8EyGATDuCda1A72qbjD3o8MfiipicmhnYmcdqoIyZzE9MlZ9FZn5sxsIJ3LPqPQj7y1jLlINwCwNSDg',
},
},
W3cVerifiableCredential
)

const w3cCredentialRecord = await w3cCredentialService.storeCredential(credential)

expect(w3cCredentialRecord).toMatchObject({
type: 'W3cCredentialRecord',
id: expect.any(String),
createdAt: expect.any(Date),
credential: expect.any(W3cVerifiableCredential),
})

expect(w3cCredentialRecord.getTags()).toMatchObject({
expandedTypes: [
'https://www.w3.org/2018/credentials#VerifiableCredential',
'https://example.org/examples#UniversityDegreeCredential',
],
})
})
})

xdescribe('sign', () => {
it('returns a signed credential', async () => {
const credential = JsonTransformer.fromJSON(
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,42 @@
import type { TagsBase } from '../../../../storage/BaseRecord'

import { Type } from 'class-transformer'
import jsonld from 'jsonld'

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

import { W3cVerifiableCredential } from './W3cVerifiableCredential'

export interface W3cCredentialRecordOptions {
id?: string
createdAt?: Date
credential: W3cVerifiableCredential
tags: CustomW3cCredentialTags
}

/**
* K-TODO: Set the appropriate tags
* @see https://github.com/hyperledger/aries-cloudagent-python/blob/e77d087bdd5f1f803616730e33d4e3f0801b5f8d/aries_cloudagent/storage/vc_holder/xform.py
*
* NOTE: Credential.type entries need to be expanded before storing them as tags
*/
export class W3cCredentialRecord extends BaseRecord {
public constructor(options: W3cCredentialRecordOptions) {
super()
if (options) {
this.credential = options.credential
}
}
export type CustomW3cCredentialTags = TagsBase & {
expandedTypes?: Array<string>
}

export class W3cCredentialRecord extends BaseRecord<TagsBase, CustomW3cCredentialTags> {
public static readonly type = 'W3cCredentialRecord'
public readonly type = W3cCredentialRecord.type

@Type(() => W3cVerifiableCredential)
public credential!: W3cVerifiableCredential

public getTags(): TagsBase {
return {
...this._tags,
public constructor(props: W3cCredentialRecordOptions) {
super()
if (props) {
this.id = props.id ?? uuid()
this.createdAt = props.createdAt ?? new Date()
this._tags = props.tags
this.credential = props.credential
}
}

public getTags() {
return this._tags
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { inject, scoped, Lifecycle } from 'tsyringe'

import { InjectionSymbols } from '../../../../constants'
import { Repository } from '../../../../storage/Repository'
import { StorageService } from '../../../../storage/StorageService'

import { W3cCredentialRecord } from './W3cCredentialRecord'

@scoped(Lifecycle.ContainerScoped)
export class W3cCredentialRepository extends Repository<W3cCredentialRecord> {
public constructor(@inject(InjectionSymbols.StorageService) storageService: StorageService<W3cCredentialRecord>) {
super(W3cCredentialRecord, storageService)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Issue Credential states as defined in RFC 0453
*
* @see https://github.com/hyperledger/aries-rfcs/blob/main/features/0453-issue-credential-v2/README.md#states
*/
export enum W3cCredentialState {
ProposalSent = 'proposal-sent',
ProposalReceived = 'proposal-received',
OfferSent = 'offer-sent',
OfferReceived = 'offer-received',
RequestSent = 'request-sent',
RequestReceived = 'request-received',
CredentialIssued = 'credential-issued',
CredentialReceived = 'credential-received',
Done = 'done',
}
1 change: 1 addition & 0 deletions packages/core/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export * from './JWE'
export * from './indyProofRequest'
export * from './VarintEncoder'
export * from './Hasher'
export * from './jsonld'
6 changes: 6 additions & 0 deletions packages/core/src/utils/jsonld.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,9 @@ export type Keyword = {
'@version': '1.1'
'@vocab': string | null
}

export const orArrayToArray = (val?: SingleOrArray<string>): Array<string> | undefined => {
if (!val) return undefined
if (Array.isArray(val)) return val
return [val]
}
10 changes: 5 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2348,6 +2348,11 @@
resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080"
integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==

"@types/digitalcredentials__jsonld@npm:@types/jsonld@^1.5.6", "@types/jsonld@^1.5.6":
version "1.5.6"
resolved "https://registry.yarnpkg.com/@types/jsonld/-/jsonld-1.5.6.tgz#4396c0b17128abf5773bb68b5453b88fc565b0d4"
integrity sha512-OUcfMjRie5IOrJulUQwVNvV57SOdKcTfBj3pjXNxzXqeOIrY2aGDNGW/Tlp83EQPkz4tCE6YWVrGuc/ZeaAQGg==

"@types/eslint@^7.2.13":
version "7.29.0"
resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.29.0.tgz#e56ddc8e542815272720bb0b4ccc2aff9c3e1c78"
Expand Down Expand Up @@ -2449,11 +2454,6 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=

"@types/jsonld@^1.5.6":
version "1.5.6"
resolved "https://registry.yarnpkg.com/@types/jsonld/-/jsonld-1.5.6.tgz#4396c0b17128abf5773bb68b5453b88fc565b0d4"
integrity sha512-OUcfMjRie5IOrJulUQwVNvV57SOdKcTfBj3pjXNxzXqeOIrY2aGDNGW/Tlp83EQPkz4tCE6YWVrGuc/ZeaAQGg==

"@types/luxon@^1.27.0":
version "1.27.1"
resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-1.27.1.tgz#aceeb2d5be8fccf541237e184e37ecff5faa9096"
Expand Down

0 comments on commit 30474cc

Please sign in to comment.