CType
A claim type (CType for short) is a KILT-specific term, but the concept is simple: A CType defines the structure of a claim. You can think of it as the data model for your claim.
Before the Attester can attest credentials, they need to decide which CType they support. For example, a traffic authority will only issue driver's licenses (=> CType for drivers license) and not trade register excerpts. Since CTypes enable interoperability between Attesters, it is highly recommended to use existing CTypes rather than creating new ones. However, for this workshop we will create our own CType.
A CType ensures that a credential contains all required attributes, e.g., a driver's license has to contain a name, date of birth, the type of vehicle that can be driven by the claimer. The CType is especially important since a Verifier would request credentials for a specific CType (e.g., the traffic police want to see your driver's license and not your gym membership).
If you want to learn more about CTypes take a look at our in depth CType documentation. You can also read through existing CTypes in our CType-index.
Creating CTypes requires an account and a full DID. Make sure your account holds KILT tokens so that you can pay the fees for creating a CType.
For example, a very basic CType for a driver's license could look like this:
{
"$id": "kilt:ctype:0x4f1d68ac46daf4613181b33b16faaf10cf94879dc2246d7485dc2ccbb843641d",
"$schema": "ipfs://bafybeiah66wbkhqbqn7idkostj2iqyan2tstc4tpqt65udlhimd7hcxjyq/",
"additionalProperties": false,
"properties": {
"age": {
"type": "integer"
},
"id": {
"type": "string"
},
"name": {
"type": "string"
}
},
"title": "Drivers License by did:kilt:4t9FPVbcN42UMxt3Z2Y4Wx38qPL8bLduAB11gLZSwn5hVEfH",
"type": "object"
}
Let's have a look at these attributes.
Key | Value |
---|---|
$id | The KILT id of this CType. It is the most important property as it represents the digital footprint of the CType. |
$schema | A reference to the meta-schema describing what a CType may look like. There are two versions. |
title | The title of the CType. |
properties | The properties that a claim conforming to this CType may have. |
type | Type is an object for all CTypes. |
additionalProperties | The default is set to false. This restricts unwanted properties in a claim. |
A CType is stored on the KILT blockchain.
In a real-life setup, a user would simply retrieve an existing CType from the chain or a repository of CTypes for example via a Credential Registry's REST API.
In this tutorial, we'll have the Attester create and attempt to store a CType on the KILT test blockchain.
Create CTypeโ
Copy the following to create a CType
with a given schema:
- Typescript
- Javascript
import * as Kilt from '@kiltprotocol/sdk-js'
// Return CType with the properties matching a given schema.
export function getCtypeSchema(): Kilt.ICType {
return Kilt.CType.fromProperties('Drivers License', {
name: {
type: 'string'
},
age: {
type: 'integer'
}
})
}
import * as Kilt from '@kiltprotocol/sdk-js'
// Return CType with the properties matching a given schema.
export function getCtypeSchema() {
return Kilt.CType.fromProperties('Drivers License', {
name: {
type: 'string',
},
age: {
type: 'integer',
},
})
}
Get CTypeโ
- Typescript
- Javascript
import { config as envConfig } from 'dotenv'
import * as Kilt from '@kiltprotocol/sdk-js'
import { generateAccount } from './generateAccount'
import { generateKeypairs } from './generateKeypairs'
import { getCtypeSchema } from './ctypeSchema'
export async function ensureStoredCtype(
attesterAccount: Kilt.KiltKeyringPair,
attesterDid: Kilt.DidUri,
signCallback: Kilt.SignExtrinsicCallback
): Promise<Kilt.ICType> {
const api = Kilt.ConfigService.get('api')
// Get the CTYPE and see if it's stored, if yes return it.
const ctype = getCtypeSchema()
try {
await Kilt.CType.verifyStored(ctype)
console.log('Ctype already stored. Skipping creation')
return ctype
} catch {
console.log('Ctype not present. Creating it now...')
// Authorize the tx.
const encodedCtype = Kilt.CType.toChain(ctype)
const tx = api.tx.ctype.add(encodedCtype)
const extrinsic = await Kilt.Did.authorizeTx(
attesterDid,
tx,
signCallback,
attesterAccount.address
)
// Write to chain then return the CType.
await Kilt.Blockchain.signAndSubmitTx(extrinsic, attesterAccount)
return ctype
}
}
// Don't execute if this is imported by another file.
if (require.main === module) {
;(async () => {
envConfig()
try {
await Kilt.connect(process.env.WSS_ADDRESS as string)
const accountMnemonic = process.env.ATTESTER_ACCOUNT_MNEMONIC as string
const { account } = generateAccount(accountMnemonic)
const didMnemonic = process.env.ATTESTER_DID_MNEMONIC as string
const { authentication, assertionMethod } = generateKeypairs(didMnemonic)
const attesterDidUri = Kilt.Did.getFullDidUriFromKey(authentication)
await ensureStoredCtype(account, attesterDidUri, async ({ data }) => ({
signature: assertionMethod.sign(data),
keyType: assertionMethod.type
}))
} catch (e) {
console.log('Error while checking on chain ctype')
throw e
}
})()
}
import { config as envConfig } from 'dotenv'
import * as Kilt from '@kiltprotocol/sdk-js'
import { generateAccount } from './generateAccount'
import { generateKeypairs } from './generateKeypairs'
import { getCtypeSchema } from './ctypeSchema'
export async function ensureStoredCtype(
attesterAccount,
attesterDid,
signCallback
) {
const api = Kilt.ConfigService.get('api')
// Get the CTYPE and see if it's stored, if yes return it.
const ctype = getCtypeSchema()
try {
await Kilt.CType.verifyStored(ctype)
console.log('Ctype already stored. Skipping creation')
return ctype
} catch {
console.log('Ctype not present. Creating it now...')
// Authorize the tx.
const encodedCtype = Kilt.CType.toChain(ctype)
const tx = api.tx.ctype.add(encodedCtype)
const extrinsic = await Kilt.Did.authorizeTx(
attesterDid,
tx,
signCallback,
attesterAccount.address
)
// Write to chain then return the CType.
await Kilt.Blockchain.signAndSubmitTx(extrinsic, attesterAccount)
return ctype
}
}
// Don't execute if this is imported by another file.
if (require.main === module) {
;(async () => {
envConfig()
try {
await Kilt.connect(process.env.WSS_ADDRESS)
const accountMnemonic = process.env.ATTESTER_ACCOUNT_MNEMONIC
const { account } = generateAccount(accountMnemonic)
const didMnemonic = process.env.ATTESTER_DID_MNEMONIC
const { authentication, assertionMethod } = generateKeypairs(didMnemonic)
const attesterDidUri = Kilt.Did.getFullDidUriFromKey(authentication)
await ensureStoredCtype(account, attesterDidUri, async ({ data }) => ({
signature: assertionMethod.sign(data),
keyType: assertionMethod.type,
}))
} catch (e) {
console.log('Error while checking on chain ctype')
throw e
}
})()
}
We'll use this to check if the CType
is on-chain already.
If yes we'll return it, otherwise we'll store it on-chain.
Remember, an account must have the required amount of tokens to pay the transaction fee and deposit.
Runโ
- Typescript
- Javascript
To run it, just execute the attester/generateCtype.ts
file.
yarn ts-node attester/generateCtype.ts
To run it, just execute the attester/generateCtype.js
file.
node attester/generateCtype.js
OK, now before we can attest Credentials, we need a Claimer to request it! Let's move on!