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: light (offchain) DIDs #408

Merged
merged 36 commits into from
Sep 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
2b23e2b
feat: add new LightDidDetails class and rename DidDetails to FullDidD…
ntn-x2 Aug 26, 2021
4d14129
feat: add function to create an off-chain DID from a seed or mnemonic
ntn-x2 Aug 26, 2021
216908c
chore: add comment as reminder to include encryption key and service …
ntn-x2 Aug 26, 2021
9c42285
feat: add encryption keys and service endpoints encoding as part of l…
ntn-x2 Aug 26, 2021
71591bf
fix: compilation errors
ntn-x2 Aug 27, 2021
609ee44
wip: add comments after sparring session
ntn-x2 Aug 27, 2021
9184544
feat: remove getNextTxIndex() from IDidDetails interface
ntn-x2 Aug 30, 2021
7a66ce9
test: unit tests for off-chain DID creation complete
ntn-x2 Aug 30, 2021
f0bab16
feat: add versioning
ntn-x2 Aug 30, 2021
4a7b139
fix: add node types to compile DID package using cbor module
ntn-x2 Aug 31, 2021
d867aae
fix: adjust cbor version to be compatible with Node 14
ntn-x2 Aug 31, 2021
e9ac34b
chore: update SDK to 0.24.0-0
ntn-x2 Aug 31, 2021
c82d99c
feat: add off-chain DIDs to DID resolver (wip)
ntn-x2 Aug 31, 2021
36c239d
test: unit tests for Full and Light DidDetails passing
ntn-x2 Aug 31, 2021
ee8e095
test: integration tests passing
ntn-x2 Aug 31, 2021
0839f8d
wip: add key encoding to light DID identifier
ntn-x2 Sep 1, 2021
a0629c0
test: unit tests for light DID resolution passing
ntn-x2 Sep 1, 2021
81b8e2f
test: all unit tests are passing
ntn-x2 Sep 1, 2021
a8f6e5f
feat: change service di to only contain the service ID and not the DI…
ntn-x2 Sep 1, 2021
2f4c0a9
test: adjust test cases and code in general (wip)
ntn-x2 Sep 1, 2021
a2607ae
chore: refactor some code, currently compiling
ntn-x2 Sep 2, 2021
89e9c47
test: all unit tests passing
ntn-x2 Sep 2, 2021
ad2f329
test: all integration tests passing
ntn-x2 Sep 2, 2021
a87ca89
chore: add documentation
ntn-x2 Sep 2, 2021
a719318
test: all unit tests passing after refactor
ntn-x2 Sep 2, 2021
b4dc7db
test: add light DID unit tests to attested claims
ntn-x2 Sep 2, 2021
7767d51
chore: lint
ntn-x2 Sep 2, 2021
e487a8d
fix: error lint
ntn-x2 Sep 2, 2021
1321493
chore: remove useless print statements in tests
ntn-x2 Sep 2, 2021
840f7bf
chore: cleaning up some stuff
ntn-x2 Sep 3, 2021
b998d1c
fix: remove v1 as default for DID version 1
ntn-x2 Sep 7, 2021
ed4d9c3
chore: apply fixes from review
ntn-x2 Sep 7, 2021
18dfe0b
chore: re-add VC export unit tests
ntn-x2 Sep 7, 2021
5e476af
chore: fix lint errors
ntn-x2 Sep 7, 2021
73a6865
test: fix failing unit test
ntn-x2 Sep 7, 2021
5630965
fix: increase jest timeout to 15s due to parachain 12s slot
ntn-x2 Sep 7, 2021
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
3 changes: 2 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ module.exports = {
testEnvironment: 'node',
clearMocks: true,
runner: 'groups',
testTimeout: 10000,
// Parachain block time is 12s
testTimeout: 15000,
setupFilesAfterEnv: ['../testingTools/setup.js'],
transformIgnorePatterns:['/node_modules/(?!@polkadot|@babel/runtime/helpers/esm/)'],
coverageThreshold: {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,6 @@
"typedoc-plugin-external-module-name": "^4.0.6",
"typescript": "^4.2.2"
},
"version": "0.22.2-2",
"version": "0.24.0-0",
"packageManager": "yarn@2.4.2"
}
2 changes: 1 addition & 1 deletion packages/chain-helpers/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@kiltprotocol/chain-helpers",
"version": "0.22.2-2",
"version": "0.24.0-0",
"description": "",
"main": "./lib/index.js",
"typings": "./lib/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion packages/config/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@kiltprotocol/config",
"version": "0.22.2-2",
"version": "0.24.0-0",
"description": "",
"main": "./lib/index.js",
"typings": "./lib/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@kiltprotocol/core",
"version": "0.22.2-2",
"version": "0.24.0-0",
"description": "",
"main": "./lib/index.js",
"typings": "./lib/index.d.ts",
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/__integrationtests__/Attestation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { BlockchainUtils, ExtrinsicErrors } from '@kiltprotocol/chain-helpers'
import {
createOnChainDidFromSeed,
DemoKeystore,
DidDetails,
FullDidDetails,
} from '@kiltprotocol/did'
import { Crypto } from '@kiltprotocol/utils'
import { randomAsHex } from '@polkadot/util-crypto'
Expand All @@ -39,9 +39,9 @@ import '../../../../testingTools/jestErrorCodeMatcher'

let tokenHolder: KeyringPair
let signer: DemoKeystore
let attester: DidDetails
let anotherAttester: DidDetails
let claimer: DidDetails
let attester: FullDidDetails
let anotherAttester: FullDidDetails
let claimer: FullDidDetails

beforeAll(async () => {
await init({ address: WS_ADDRESS })
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/__integrationtests__/Ctypes.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type { ICType } from '@kiltprotocol/types'
import { BlockchainUtils, ExtrinsicErrors } from '@kiltprotocol/chain-helpers'
import { KeyringPair } from '@polkadot/keyring/types'
import {
DidDetails,
FullDidDetails,
DemoKeystore,
createOnChainDidFromSeed,
} from '@kiltprotocol/did'
Expand All @@ -31,7 +31,7 @@ beforeAll(async () => {
})

describe('When there is an CtypeCreator and a verifier', () => {
let ctypeCreator: DidDetails
let ctypeCreator: FullDidDetails
let paymentAccount: KeyringPair
let ctypeCounter = 0
const keystore = new DemoKeystore()
Expand Down
14 changes: 7 additions & 7 deletions packages/core/src/__integrationtests__/Delegation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import type { KeyringPair } from '@polkadot/keyring/types'
import {
createOnChainDidFromSeed,
DemoKeystore,
DidDetails,
FullDidDetails,
} from '@kiltprotocol/did'
import { randomAsHex } from '@polkadot/util-crypto'
import Attestation from '../attestation/Attestation'
Expand All @@ -30,12 +30,12 @@ import { getAttestationHashes } from '../delegation/DelegationNode.chain'

let paymentAccount: KeyringPair
let signer: DemoKeystore
let root: DidDetails
let claimer: DidDetails
let attester: DidDetails
let root: FullDidDetails
let claimer: FullDidDetails
let attester: FullDidDetails

async function writeHierarchy(
delegator: DidDetails,
delegator: FullDidDetails,
ctypeHash: ICType['hash']
): Promise<DelegationNode> {
const rootNode = DelegationNode.newRoot({
Expand All @@ -60,8 +60,8 @@ async function writeHierarchy(
async function addDelegation(
hierarchyId: IDelegationNode['id'],
parentId: DelegationNode['id'],
delegator: DidDetails,
delegee: DidDetails,
delegator: FullDidDetails,
delegee: FullDidDetails,
permissions: Permission[] = [Permission.ATTEST, Permission.DELEGATE]
): Promise<DelegationNode> {
const delegationNode = DelegationNode.newNode({
Expand Down
14 changes: 7 additions & 7 deletions packages/core/src/__integrationtests__/Did.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,15 @@ describe('write and didDeleteTx', () => {
await expect(DidChain.queryById(didIdentifier)).resolves.toMatchObject<
Partial<DidTypes.IDidChainRecordJSON>
>({
did: DidUtils.getKiltDidFromIdentifier(didIdentifier),
did: DidUtils.getKiltDidFromIdentifier(didIdentifier, 'full'),
})
}, 30_000)

it('deletes DID from previous step', async () => {
await expect(DidChain.queryById(didIdentifier)).resolves.toMatchObject<
Partial<DidTypes.IDidChainRecordJSON>
>({
did: DidUtils.getKiltDidFromIdentifier(didIdentifier),
did: DidUtils.getKiltDidFromIdentifier(didIdentifier, 'full'),
})

const call = await DidChain.getDeleteDidExtrinsic()
Expand Down Expand Up @@ -132,7 +132,7 @@ it('creates and updates DID', async () => {
await expect(DidChain.queryById(didIdentifier)).resolves.toMatchObject<
Partial<DidTypes.IDidChainRecordJSON>
>({
did: DidUtils.getKiltDidFromIdentifier(didIdentifier),
did: DidUtils.getKiltDidFromIdentifier(didIdentifier, 'full'),
endpointData: {
urls: ['https://example.com'],
contentType: 'application/json',
Expand Down Expand Up @@ -164,7 +164,7 @@ it('creates and updates DID', async () => {
await expect(DidChain.queryById(didIdentifier)).resolves.toMatchObject<
Partial<DidTypes.IDidChainRecordJSON>
>({
did: DidUtils.getKiltDidFromIdentifier(didIdentifier),
did: DidUtils.getKiltDidFromIdentifier(didIdentifier, 'full'),
endpointData: {
urls: ['ftp://example.com/abc'],
contentType: 'application/ld+json',
Expand All @@ -181,7 +181,7 @@ describe('DID authorization', () => {
const { publicKey, alg } = await keystore.generateKeypair({
alg: SigningAlgorithms.Ed25519,
})
didIdentifier = encodeAddress(publicKey)
didIdentifier = encodeAddress(publicKey, 38)
key = { publicKey, type: alg }
const tx = await DidChain.generateCreateTx({
didIdentifier,
Expand All @@ -202,13 +202,13 @@ describe('DID authorization', () => {
await expect(DidChain.queryById(didIdentifier)).resolves.toMatchObject<
Partial<DidTypes.IDidChainRecordJSON>
>({
did: DidUtils.getKiltDidFromIdentifier(didIdentifier),
did: DidUtils.getKiltDidFromIdentifier(didIdentifier, 'full'),
})
}, 30_000)

beforeEach(async () => {
lastTxIndex = await DidChain.queryLastTxIndex(
DidUtils.getKiltDidFromIdentifier(didIdentifier)
DidUtils.getKiltDidFromIdentifier(didIdentifier, 'full')
)
})

Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/__integrationtests__/ErrorHandler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { KeyringPair } from '@polkadot/keyring/types'
import {
createOnChainDidFromSeed,
DemoKeystore,
DidDetails,
FullDidDetails,
} from '@kiltprotocol/did'
import { randomAsHex } from '@polkadot/util-crypto'
import { Attestation } from '..'
Expand All @@ -26,7 +26,7 @@ import { addressFromRandom, devAlice, WS_ADDRESS } from './utils'
import '../../../../testingTools/jestErrorCodeMatcher'

let paymentAccount: KeyringPair
let someDid: DidDetails
let someDid: FullDidDetails
const keystore = new DemoKeystore()

beforeAll(async () => {
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/attestation/Attestation.chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ function decode(
claimHash,
cTypeHash: chainAttestation.ctypeHash.toString(),
owner: DidUtils.getKiltDidFromIdentifier(
chainAttestation.attester.toString()
chainAttestation.attester.toString(),
'full'
),
delegationId:
chainAttestation.delegationId.unwrapOr(null)?.toString() || null,
Expand Down
73 changes: 70 additions & 3 deletions packages/core/src/attestedclaim/AttestedClaim.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ import type {
IDidDetails,
IDidResolver,
} from '@kiltprotocol/types'
import { DemoKeystore, createLocalDemoDidFromSeed } from '@kiltprotocol/did'
import {
DemoKeystore,
createLocalDemoDidFromSeed,
createLightDidFromSeed,
} from '@kiltprotocol/did'
import { UUID } from '@kiltprotocol/utils'
import Attestation from '../attestation/Attestation'
import Claim from '../claim/Claim'
Expand Down Expand Up @@ -120,7 +124,7 @@ describe('RequestForAttestation', () => {
]
})

it('verify attested claims', async () => {
it('verify attested claims signed by a full DID', async () => {
const attestedClaim = await buildAttestedClaim(
identityCharlie,
identityAlice,
Expand All @@ -141,6 +145,28 @@ describe('RequestForAttestation', () => {
AttestedClaim.verify(attestedClaim, { claimerDid: identityCharlie })
).resolves.toBe(true)
})
it('verify attested claims signed by a light DID', async () => {
const identityDave = await createLightDidFromSeed(keystore, '//Dave')
const attestedClaim = await buildAttestedClaim(
identityDave,
identityAlice,
{
a: 'a',
b: 'b',
c: 'c',
},
[legitimation],
keystore
)

;(query as jest.Mock).mockResolvedValue(attestedClaim.attestation)

// check proof on complete data
expect(AttestedClaim.verifyData(attestedClaim)).toBeTruthy()
await expect(
AttestedClaim.verify(attestedClaim, { claimerDid: identityDave })
).resolves.toBe(true)
})

it('compresses and decompresses the attested claims object', () => {
expect(AttestedClaimUtils.compress(legitimation)).toEqual(
Expand Down Expand Up @@ -269,7 +295,7 @@ describe('create presentation', () => {
expect(cred).toBeDefined()
})

it('should create presentation and exclude specific attributes', async () => {
it('should create presentation and exclude specific attributes using a full DID', async () => {
const mockResolver: IDidResolver = {
resolveDoc: async (did: string) => {
if (did.startsWith(claimer.did)) return claimer
Expand All @@ -293,6 +319,47 @@ describe('create presentation', () => {
).resolves.toBe(true)
expect(att.request.claimerSignature?.challenge).toEqual(challenge)
})
it('should create presentation and exclude specific attributes using a light DID', async () => {
claimer = await createLightDidFromSeed(keystore, '//Attester')
const rawCType: ICType['schema'] = {
$id: 'kilt:ctype:0x1',
$schema: 'http://kilt-protocol.org/draft-01/ctype#',
title: 'credential',
properties: {
name: { type: 'string' },
},
type: 'object',
}
ctype = CType.fromSchema(rawCType, attester.did)

// cannot be used since the variable needs to be established in the outer scope
reqForAtt = RequestForAttestation.fromClaim(
Claim.fromCTypeAndClaimContents(
ctype,
{
name: 'Peter',
age: 12,
},
claimer.did
)
)

attestation = Attestation.fromRequestAndDid(reqForAtt, attester.did)
;(query as jest.Mock).mockResolvedValue(attestation)

const cred = AttestedClaim.fromRequestAndAttestation(reqForAtt, attestation)

const challenge = UUID.generate()
const att = await cred.createPresentation({
selectedAttributes: ['name'],
signer: keystore,
claimerDid: claimer,
challenge,
})
expect(att.getAttributes()).toEqual(new Set(['name']))
await expect(AttestedClaim.verify(att)).resolves.toBe(true)
expect(att.request.claimerSignature?.challenge).toEqual(challenge)
})

it('should get attribute keys', async () => {
const cred = AttestedClaim.fromRequestAndAttestation(reqForAtt, attestation)
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/ctype/CType.chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export async function store(ctype: ICType): Promise<SubmittableExtrinsic> {
export function decode(encoded: Option<AccountId>): IDidDetails['did'] | null {
DecoderUtils.assertCodecIsType(encoded, ['Option<CtypeCreatorOf>'])
return encoded.isSome
? DidUtils.getKiltDidFromIdentifier(encoded.unwrap().toString())
? DidUtils.getKiltDidFromIdentifier(encoded.unwrap().toString(), 'full')
: null
}

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/ctype/CType.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ describe('CType', () => {
`)
expect(() =>
CType.fromCType(faultyAddressTypeCType)
).toThrowErrorMatchingInlineSnapshot(`"Not a KILT did: 4262626426"`)
).toThrowErrorMatchingInlineSnapshot(`"Not a valid KILT did: 4262626426"`)
expect(() =>
CType.fromCType(wrongSchemaIdCType)
).toThrowErrorMatchingInlineSnapshot(
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/delegation/DelegationDecoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ export function decodeDelegationNode(
: undefined,
childrenIds: [...delegationNode.children.keys()].map((id) => id.toHex()),
account: DidUtils.getKiltDidFromIdentifier(
delegationNode.details.owner.toString()
delegationNode.details.owner.toString(),
'full'
),
permissions: decodePermissions(
delegationNode.details.permissions.toNumber()
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/delegation/DelegationNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
} from '@kiltprotocol/types'
import { Crypto, SDKErrors, UUID } from '@kiltprotocol/utils'
import { ConfigService } from '@kiltprotocol/config'
import { DidTypes, DidDetailsUtils } from '@kiltprotocol/did'
import { DidTypes, DidUtils } from '@kiltprotocol/did'
import type { DelegationHierarchyDetailsRecord } from './DelegationDecoder'
import { query as queryAttestation } from '../attestation/Attestation.chain'
import {
Expand Down Expand Up @@ -275,7 +275,7 @@ export default class DelegationNode implements IDelegationNode {
delegeeDid: IDidDetails,
signer: KeystoreSigner
): Promise<DidTypes.SignatureEnum> {
const { alg, signature } = await DidDetailsUtils.signWithDid(
const { alg, signature } = await DidUtils.signWithDid(
Crypto.coToUInt8(this.generateHash()),
delegeeDid,
signer,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import type {
} from '@kiltprotocol/types'
import { KeyRelationship } from '@kiltprotocol/types'
import { Crypto, SDKErrors } from '@kiltprotocol/utils'
import { DefaultResolver, DidUtils, DidDetailsUtils } from '@kiltprotocol/did'
import { DefaultResolver, DidUtils } from '@kiltprotocol/did'
import ClaimUtils from '../claim/Claim.utils'
import AttestedClaim from '../attestedclaim/AttestedClaim'
import RequestForAttestationUtils from './RequestForAttestation.utils'
Expand Down Expand Up @@ -320,7 +320,7 @@ export default class RequestForAttestation implements IRequestForAttestation {
did: IDidDetails,
challenge?: string
): Promise<this> {
const { signature, keyId } = await DidDetailsUtils.signWithDid(
const { signature, keyId } = await DidUtils.signWithDid(
makeSigningData(this, challenge),
did,
signer,
Expand All @@ -334,7 +334,7 @@ export default class RequestForAttestation implements IRequestForAttestation {
key: IDidKeyDetails,
challenge?: string
): Promise<this> {
const { signature } = await DidDetailsUtils.signWithKey(
const { signature } = await DidUtils.signWithKey(
makeSigningData(this, challenge),
key,
signer
Expand Down
Loading