Skip to main content

DID

Now it's time to generate a DID using the previously created account for the Attester.

A DID may represent any entity, which could be a person, an organization or a machine.

A KILT decentralized identifier (DID) is a string uniquely identifying each KILT user. You can store information about your DID on the KILT chain. This is useful for many different use cases. One example would be messaging. You would store a public encryption key and a services on-chain, which can both be queried using your DID. Other users can now encrypt messages using your public encryption key and send the message to your service.

There are two types of DIDs: light and full. Take a look at our DID documentation to learn more about DIDs and the difference between the light and full versions.

KILT DID

There are currently four different key types that a DID supports:

  • An authentication keypair, used to sign claims and create authenticated credential presentations
  • A key-agreement keypair, used to encrypt/decrypt messages
  • An assertion-method keypair, used to write CTypes and attestations on chain
  • A capability-delegation keypair, used to write delegations on chain

Keys can be replaced over time, e.g., if a key is compromised.

Account vs DIDโ€‹

A full DID needs to be registered on the blockchain. For that, an account has to submit the DID creation transaction. There is always an account that submits the transactions and pays for the fees and the DID that authorized the call. Because the DID and the account are not connected, DIDs do not hold any coins.

Create a DIDโ€‹

To create a DID we can use the same keyrings that are used to generate accounts. For our Attester we'll need all four types of keys. Since three of the key types are used for signatures, we can use the same key for these. We'll use the default KILT keyring to generate them.

attester/generateKeypairs.ts
import * as Kilt from '@kiltprotocol/sdk-js'
import {
blake2AsU8a,
keyExtractPath,
keyFromPath,
mnemonicGenerate,
mnemonicToMiniSecret,
sr25519PairFromSeed
} from '@polkadot/util-crypto'
import { generateAccount } from './generateAccount'

// Because there is no first-class support for this class of keys,
// we need to use a workaround to generate a key we can use for encryption/decryption.
function generateKeyAgreement(mnemonic: string) {
const secretKeyPair = sr25519PairFromSeed(mnemonicToMiniSecret(mnemonic))
const { path } = keyExtractPath('//did//keyAgreement//0')
const { secretKey } = keyFromPath(secretKeyPair, path, 'sr25519')
return Kilt.Utils.Crypto.makeEncryptionKeypairFromSeed(blake2AsU8a(secretKey))
}

export function generateKeypairs(mnemonic = mnemonicGenerate()) {
const { account } = generateAccount(mnemonic)

const authentication = {
...account.derive('//did//0'),
type: 'sr25519'
} as Kilt.KiltKeyringPair

const assertionMethod = {
...account.derive('//did//assertion//0'),
type: 'sr25519'
} as Kilt.KiltKeyringPair

const capabilityDelegation = {
...account.derive('//did//delegation//0'),
type: 'sr25519'
} as Kilt.KiltKeyringPair

const keyAgreement = generateKeyAgreement(mnemonic)

return {
authentication: authentication,
keyAgreement: keyAgreement,
assertionMethod: assertionMethod,
capabilityDelegation: capabilityDelegation
}
}

Once we have created all the necessary keys for a DID we can create the on-chain DID. To create a DID we first initialize everything. After that, we load the account that we created in the last section. The account will be used to pay for the DID registration. Finally, we create and submit the extrinsic (aka transaction) that will register our DID.

attester/generateDid.ts
import { config as envConfig } from 'dotenv'

import * as Kilt from '@kiltprotocol/sdk-js'

import { generateAccount } from './generateAccount'
import { generateKeypairs } from './generateKeypairs'

export async function createFullDid(
submitterAccount: Kilt.KiltKeyringPair
): Promise<{
mnemonic: string
fullDid: Kilt.DidDocument
}> {
const api = Kilt.ConfigService.get('api')

const mnemonic = Kilt.Utils.Crypto.mnemonicGenerate()
const {
authentication,
keyAgreement,
assertionMethod,
capabilityDelegation
} = generateKeypairs(mnemonic)
// Get tx that will create the DID on chain and DID-URI that can be used to resolve the DID Document.
const fullDidCreationTx = await Kilt.Did.getStoreTx(
{
authentication: [authentication],
keyAgreement: [keyAgreement],
assertionMethod: [assertionMethod],
capabilityDelegation: [capabilityDelegation]
},
submitterAccount.address,
async ({ data }) => ({
signature: authentication.sign(data),
keyType: authentication.type
})
)

await Kilt.Blockchain.signAndSubmitTx(fullDidCreationTx, submitterAccount)

const didUri = Kilt.Did.getFullDidUriFromKey(authentication)
const encodedFullDid = await api.call.did.query(Kilt.Did.toChain(didUri))
const { document } = Kilt.Did.linkedInfoFromChain(encodedFullDid)

if (!document) {
throw new Error('Full DID was not successfully created.')
}

return { mnemonic, fullDid: document }
}

// 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)

// Load attester account
const accountMnemonic = process.env.ATTESTER_ACCOUNT_MNEMONIC as string
const { account } = generateAccount(accountMnemonic)
const { mnemonic, fullDid } = await createFullDid(account)

console.log('\nsave following to .env to continue\n')
console.error(`ATTESTER_DID_MNEMONIC="${mnemonic}"\n`)
console.error(`ATTESTER_DID_URI="${fullDid.uri}"\n`)
} catch (e) {
console.log('Error while creating attester DID')
throw e
}
})()
}

Executeโ€‹

You can now execute the script with:

yarn ts-node ./attester/generateDid.ts

Once you have executed the script, the output should provide you with your ATTESTER_DID_MNEMONIC and ATTESTER_DID_URI. Your output should look like this (but it won't be identical since the DIDs are constructed from your account):

ATTESTER_DID_MNEMONIC="beyond large galaxy...
ATTESTER_DID_URI="did:kilt:4ohMvUHsyeD..."

Be sure to save it in your .env file. It should now look similar to this:

.env
WSS_ADDRESS=wss://peregrine.kilt.io

ATTESTER_ACCOUNT_MNEMONIC="warrior icon use cry...
ATTESTER_ACCOUNT_ADDRESS=4ohMvUHsyeDhMVZF...
ATTESTER_DID_MNEMONIC="beyond large galaxy...
ATTESTER_DID_URI="did:kilt:4ohMvUHsyeD..."

Well done - You've successfully generated a full DID! Let's create a CType!