Skip to main content

DID

The next step is to generate a KILT decentralized identifier (DID) using the account you created for the Attester in the previous step.

A DID may represent any entity, such as a person, an organization, or a machine.

A DID is a string uniquely identifying each KILT user. You can store information about a DID on the KILT chain, which is useful for different use cases.

One use case is messaging. You could store a public encryption key and a service on chain, and a user can query both using a DID. Other users can now encrypt messages using your public encryption key and send a message to your service.

Light and full DIDsโ€‹

Kilt supports two DID types: light and full.

There are differences between the two types, but the most crucial is that you can use a light DID offline, but a full DID needs access to the blockchain to work. Read the DID documentation to learn more about the difference between the light and full types.

KILT DID

A DID supports four different key types:

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

You can replace keys over time, e.g., if a key becomes compromised.

What's the difference between a DID and an account?โ€‹

A DID and an account sound quite similar, but there are some differences:

  • You record both to chain
  • You can have a DID without an account
  • You can have an account without a DID
  • Only an account can pay deposits and fees and attest claims
  • DIDs don't hold any coins

In summary, you register a DID on the blockchain by an account submitting the DID creation transaction and paying the fees.

Create a DIDโ€‹

As an Attester needs to interact with the chain, you must create a full DID.

Write DID to chainโ€‹

The KILT SDK provides multiple methods to create DIDs, this workshop highlights the createFromAccount method, that creates a DID from any pre-existing substrate-compatible account.

Bring your own account

This workshop assumes you followed the create account step, but if you have a pre-existing account, you can use that instead.

Create and submit the extrinsic (aka transaction) that registers the DID.

attester/generateDid.ts
import * as Kilt from '@kiltprotocol/sdk-js'
import { config as envConfig } from 'dotenv'
import { generateAccount } from './generateAccount'

export async function createFullDid(
creatorAccount: Kilt.KiltKeyringPair & {
type: 'ed25519' | 'sr25519' | 'ecdsa'
}
): Promise<{
fullDid: Kilt.DidDocument
}> {
const api = Kilt.ConfigService.get('api')

const verificationMethod = Kilt.Did.publicKeyToChain(creatorAccount)

const txs = [
api.tx.did.createFromAccount(verificationMethod),
api.tx.did.dispatchAs(
creatorAccount.address,
api.tx.did.setAttestationKey(verificationMethod)
)
]

console.log('Creating DID from accountโ€ฆ')
await Kilt.Blockchain.signAndSubmitTx(
api.tx.utility.batch(txs),
creatorAccount
)
const didUri = Kilt.Did.getFullDidUriFromKey(creatorAccount)
const encodedFullDid = await api.call.did.query(Kilt.Did.toChain(didUri))
const { document: didDocument } = Kilt.Did.linkedInfoFromChain(encodedFullDid)

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

return { fullDid: didDocument }
}

// 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 { fullDid } = await createFullDid(account)

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

The publicKeyToChain helper method returns a public key of the correct type.

The txs array holds the two transactions containing the extrinsics needed to submit to the chain for the Attester's DID creation.

The createFromAccount method takes the authenticated key of the account to attach the DID to, and the setAttestationKey method takes the same parameter to set the attestation key the DID needs and uses.

An Attester account needs to have an attestation key to write CTypes and attestations on chain. Use the setAttestationKey method to set this. For this example transaction, the Attester account uses the dispatchAs proxy method to assign the attestation key to the same account. However, you can also use this method to assign the attestation key to another account.

The signAndSubmitTx method then takes those transactions and submits them as a batch to the chain.

Run the codeโ€‹

Now run the code with:

yarn ts-node ./attester/generateDid.ts

Once you have run the script, the output should provide you with the ATTESTER_DID_URI.

The output should look like the following, but not identical since the code creates the DIDs from your account:

ATTESTER_DID_URI="did:kilt:4ohMvUHsyeDโ€ฆ"

Save the values in the .env file, which should now look like the following:

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

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

Well done - You've generated a full DID! The next step is to create a CType!

Generate Keysโ€‹

Add the following code to the generateKeypairs file.

attester/generateKeypairs.ts
import * as Kilt from '@kiltprotocol/sdk-js'

import { mnemonicGenerate } from '@polkadot/util-crypto'

export function generateKeypairs(mnemonic = mnemonicGenerate()): {
authentication: Kilt.KiltKeyringPair
keyAgreement: Kilt.KiltEncryptionKeypair
assertionMethod: Kilt.KiltKeyringPair
capabilityDelegation: Kilt.KiltKeyringPair
} {
const authentication = Kilt.Utils.Crypto.makeKeypairFromUri(mnemonic)

const assertionMethod = Kilt.Utils.Crypto.makeKeypairFromUri(mnemonic)

const capabilityDelegation = Kilt.Utils.Crypto.makeKeypairFromUri(mnemonic)

const keyAgreement = Kilt.Utils.Crypto.makeEncryptionKeypairFromSeed(
Kilt.Utils.Crypto.mnemonicToMiniSecret(mnemonic)
)

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