Skip to main content

CType

A claim type (CType) is a KILT-specific term, but the concept is simple: A CType is a JSON schema that defines the structure of a claim, and you can think of it as the data model for your claim.

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, and the vehicle types that the claimer can drive. The CType is important since a Verifier requests credentials for a specific CType. For example, the traffic police want to see your driver's license, not your gym membership.

To learn more about CTypes, read the in-depth CType documentation. You can also read through existing CTypes in the CType-index.

Before the Attester can attest credentials, they must decide which CType they support. For example, a traffic authority only issues driver's licenses (A CType for driver's license), not a university diploma.

Since CTypes enable interoperability between Attesters, using existing CTypes rather than creating new ones is highly recommended. However, this workshop creates a new CType to show the process.

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 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"
}

The CType has the following attributes:

KeyValue
$idThe KILT id of this CType. It's the most important property as it represents the digital footprint of the CType.
$schemaA reference to the meta-schema describing what a CType may look like. There are two versions.
titleThe title of the CType.
propertiesThe properties that a claim conforming to this CType may have.
typeType is an object for all CTypes.
additionalPropertiesThe default is false. This restricts unwanted properties in a claim.

A CType is stored on the KILT blockchain.

In a real-world situation, a user would retrieve an existing CType from the chain or a CType registry. For example, via a Credential Registry's REST API.

In this tutorial, the Attester creates and attempts to store a CType on the KILT test blockchain.

Create CTypeโ€‹

Copy the following to define a CType with a given schema:

attester/ctypeSchema.ts
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'
}
})
}
warning

As many people follow this workshop, using the CType schema defined above will result in a duplicate error when you run the code later. To avoid this, change the value of fromProperties to something unique, such as adding your name to the "Drivers License" string.

Get CTypeโ€‹

Copy the following to create a CType on the chain:

attester/generateCtype.ts
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)

const newCType = await ensureStoredCtype(
account,
attesterDidUri,
async ({ data }) => ({
signature: assertionMethod.sign(data),
keyType: assertionMethod.type
})
)

console.log(
`your ctype was succsesfully created\n\n${JSON.stringify(
newCType,
null,
2
)}`
)
} catch (e) {
console.log('Error while checking on chain ctype')
throw e
}
})()
}

The ensureStoredCType function takes the Attester's account, DID, and a callback to sign the function and checks if the CType is already on chain. It uses the verifyStored method to pass the CType to the KILT blockchain and make the check. If it does not exist, it stores it on chain, using the toChain method to encode the CType into a unique hash and the add method to create a new CType from the given unique hash and associate it with the Attester. The function then uses the authorizeTx to authorize the transaction and signAndSubmitTx to sign and submit the transaction containing the new CType.

warning

Remember, an account must have the required amount of tokens to pay the transaction fee and deposit.

Runโ€‹

Run the attester/generateCtype.ts file.

yarn ts-node attester/generateCtype.ts

Before you can attest Credentials, you need a Claimer to request it