From b6d1a37f2dd82b8b9692f8a12cf6df300fa01339 Mon Sep 17 00:00:00 2001 From: dudleyneedham Date: Wed, 14 May 2025 10:14:28 +0200 Subject: [PATCH] feat: adding the draft dids from chris --- docs/sdk/04_dids.md | 441 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 441 insertions(+) diff --git a/docs/sdk/04_dids.md b/docs/sdk/04_dids.md index fff66ad47..b6b7b3105 100644 --- a/docs/sdk/04_dids.md +++ b/docs/sdk/04_dids.md @@ -2,3 +2,444 @@ id: dids title: DID --- + +A Decentralized Identifier (DID) is a string uniquely identifying each KILT user. +A DID may represent any entity, such as a person, an organization, or a machine. +You can store information about a DID on the KILT chain, which is useful for different use cases. + +The `Kilt.DidHelpers` namespace of the KILT SDK provides the methods to create, update, and delete DIDs on the KILT blockchain. + +:::info Light DIDs + +Older versions of the KILT SDK supported creating "light DIDs," usable offline with no connection with the KILT blockchain. + +This is no longer recommended, but [you can use pre-version 1.0 of the SDK to create them](/docs/0.3/sdk/cookbook/dids/light-did-creation) if you need to. + +::: + +## Create + +:::info + +Use the `createDid` method from the `DidHelpers` class to create a DID on the chain. + +[Read more in the API documentation](https://kiltprotocol.github.io/sdk-js/functions/sdk_js_src.DidHelpers.createDid.html). + +::: + + + +```ts +const transactionHandler = Kilt.DidHelpers.createDid({ + api, + signers: [authenticationKeyPair], + submitter: submitterAccount, + fromPublicKey: authenticationKeyPair.publicKeyMultibase +}) + +const didDocumentTransactionResult = await transactionHandler.submit() +``` + +The KILT SDK provides the `createDid` method from the `DidHelpers` class to create a DID on the chain. +It takes the following parameters: + +- `api`: The connection to the KILT blockchain. +- `signers`: An array of keys used for verification methods in the DID Document. For creating a DID, you only need the key for the authentication verification method. +- `submitter`: The account used to submit the transaction to the blockchain. + + :::caution + + The submitter account must have enough funds to cover the required storage deposit. + + ::: + +- `fromPublicKey`: The public key that features as the DID's initial authentication method and determines the DID identifier. + +The method returns a `TransactionHandler` type, which includes two methods: + +- `submit`: Submits a transaction for inclusion in a block on the blockchain. + + :::info + + The `submit()` method by default, waits for the block to be finalized. + [You can override this behavior](https://kiltprotocol.github.io/sdk-js/interfaces/types_src.TransactionHandlers.html) by passing `false` to the `awaitFinalized` named parameter of the `submit` object. + + ::: + +## Derivation paths + +The code example below derives different types of keys from a single account using derivation paths. + +A derivation path is a way to derive a new key from a parent key and is a sequence of indices separated by a delimiter. +The most common delimiter is `/` (forward slash). + +KILT uses the same derivation paths as the underlying Polkadot libraries, using hard key derivation. + +#### Hard derivation + +A hard derivation path does not allow someone to do either of these. +Even if you know a derived private key, it's not possible to figure out the private key of the root address, and it's impossible to prove that the first account is linked with the second. + +A `//` (double slash) indicates a hard derivation path. +For example, `deal rice sunny now boss cluster team use wreck electric wing deliver//0` is a hard derivation path. + +To create another account using the same seed, change the number at the end of the string. For example, `/1`, `/2`, and `/3` create different derived accounts. + +Using derivation paths simplifies key management, ensuring that a single mnemonic seed serves as the basis for multiple keys associated with a DID. +This method improves efficiency while maintaining security. +However, it's essential to handle and store private keys securely to prevent unauthorized access and ensure the overall integrity and privacy of the decentralized identity system. + +Below is an example code snippet illustrating the key pair generation for a KILT DID: + +```ts +const transactionHandler = Kilt.DidHelpers.createDid({ + api, + signers: [authenticationKeyPair], + submitter: submitterAccount, + fromPublicKey: authenticationKeyPair.publicKeyMultibase +}) + +const didDocumentTransactionResult = await transactionHandler.submit() +``` + +:::info +This example doesn't show how to store the keys. +It is recommended to store the keys in a secure manner, e.g. only storing the private keys encrypted on disk. +The mnemonic seed phrase can be used to regenerate the keys, so it is recommended to also store the mnemonic in a secure manner and create a backup of it. +::: + +## Update a DID + +Once anchored to the KILT blockchain, you can update a full DID. +The SDK doesn't have a specific method for updating a DID, instead you use relevant methods to update the DID. + +### Services + +#### Add a service + +:::info + +Use the `addService` method from the `DidHelpers` class to add a service to a DID. + +[Read more in the API documentation](https://kiltprotocol.github.io/sdk-js/functions/sdk_js_src.DidHelpers.addService.html). + +::: + +```ts +const transactionHandler = Kilt.DidHelpers.addService({ + api, + submitter, + signers: [didKeypair], + didDocument, + // TODO: change service endpoint. + service: { + id: '#email_service', + type: ['https://schema.org/email'], + serviceEndpoint: ['mailto:info@kilt.io'], + }, + }) + +const didDocumentTransactionResult = await transactionHandler.submit() +``` + +#### Remove a service + +:::info + +Use the `removeService` method from the `DidHelpers` class to add a service to a DID. + +[Read more in the API documentation](https://kiltprotocol.github.io/sdk-js/functions/sdk_js_src.DidHelpers.removeService.html). + +::: + +```ts +const transactionHandler = Kilt.DidHelpers.addService({ + api, + submitter, + signers: [didKeypair], + didDocument, + // TODO: change service endpoint. + service: { + id: '#email_service', + type: ['https://schema.org/email'], + serviceEndpoint: ['mailto:info@kilt.io'], + }, + }) + +const didDocumentTransactionResult = await transactionHandler.submit() +``` + +## Delete + +Once a DID is no longer needed, it is recommended to deactivate it by removing it from the KILT blockchain. +The following snippet shows how to do it: + +```ts +const transactionHandler = Kilt.DidHelpers.createDid({ + api, + signers: [authenticationKeyPair], + submitter: submitterAccount, + fromPublicKey: authenticationKeyPair.publicKeyMultibase +}) + +const didDocumentTransactionResult = await transactionHandler.submit() +``` + +:::warning +Please note that once deleted, a full DID becomes unusable and cannot be re-created anymore. +This means that all credentials obtained with that DID are no longer valid and must be obtained with a different DID if needed. +::: + +### Claim back a DID deposit + +Claiming back the deposit of a DID is semantically equivalent to deactivating and deleting the DID, with the difference that the extrinsic to claim the deposit can only be called by the deposit owner and does not require a signature by the DID subject: + +```ts +const transactionHandler = Kilt.DidHelpers.createDid({ + api, + signers: [authenticationKeyPair], + submitter: submitterAccount, + fromPublicKey: authenticationKeyPair.publicKeyMultibase +}) + +const didDocumentTransactionResult = await transactionHandler.submit() +``` + +## Query + +Querying the state of a DID is called **resolution**. +The entity that queries the DID Document for a given DID, i.e., resolves it, is called a **resolver**. + +The KILT SDK provides such a resolver to use with KILT DIDs, as the snippet below shows: + +```ts +const transactionHandler = Kilt.DidHelpers.createDid({ + api, + signers: [authenticationKeyPair], + submitter: submitterAccount, + fromPublicKey: authenticationKeyPair.publicKeyMultibase +}) + +const didDocumentTransactionResult = await transactionHandler.submit() +``` + +:::note +The DID resolver can resolve both light and full DIDs. +For a more in-depth explanation about the KILT DID method and resolution, refer to our [specification](https://github.com/KILTprotocol/spec-kilt-did). +::: + +## Generate DID keys + + + +Creating a Decentralized Identifier (DID) on the KILT network involves generating keying material for authentication and encryption. +This guide shows how to create a set of key pairs suitable for generating a KILT DID. + +Before proceeding, it's important to note that this example assumes the usage of the `@kiltprotocol/sdk-js` library along with the `@polkadot/util-crypto` library for cryptographic operations. + +Additionally, it's important to securely store keys and the mnemonic seed phrase. +For production use, ensure that private keys are encrypted and stored safely, while also creating a backup of the mnemonic seed phrase. + +## Build DID Extrinsics + +DID keys can be used to sign extrinsic. +But not every extrinsic can be signed using a DID. +The Spiritnet blockchain offers two types of extrinsics. + +The first type can only be called using an account. +We call them account extrinsic. +The second callable type are DID extrinsics. +They must be used for all KILT features like creating CTypes, issue attestations, etc. +Since every extrinsic requires fees to be paid, this type needs to be wrapped inside an account extrinsic. +Accounts hold balances and can therefore pay fees and provide deposits. + +This document describes how to sign the DID extrinsics. +The KILT SDK provides two functions for signing DID extrinsics. +The first function signs a single extrinsic while the second one batches multiple extrinsics together. + +## Single extrinsics + +To sign a single extrinsic, you need to provide: + +- the DID that wants to sign the extrinsic (also called _origin_ of the extrinsic) + + +- the extrinsic that should be signed and submitted +- and the address of the account that pays for the fees. + +```ts +const transactionHandler = Kilt.DidHelpers.createDid({ + api, + signers: [authenticationKeyPair], + submitter: submitterAccount, + fromPublicKey: authenticationKeyPair.publicKeyMultibase +}) + +const didDocumentTransactionResult = await transactionHandler.submit() +``` + +## Batch multiple extrinsics + +Full DIDs can also be used to batch multiple extrinsics that require the signature of the DID. +For instance, a batch could create multiple services with a single submission to the blockchain. +This would save the user the time of generating one additional signature, as multiple extrinsics are batched and signed at once. +The extrinsics are also submitted and executed in the same block. +For more information, see the [official Substrate documentation](https://paritytech.github.io/substrate/master/pallet_utility/pallet/struct.Pallet.html). + +An example of a batch using the `authorizeBatch` is provided below. + +```ts +const transactionHandler = Kilt.DidHelpers.createDid({ + api, + signers: [authenticationKeyPair], + submitter: submitterAccount, + fromPublicKey: authenticationKeyPair.publicKeyMultibase +}) + +const didDocumentTransactionResult = await transactionHandler.submit() +``` + +DIDs have different keys that posses different capabilities. +Each key can only be used to authorize a specific subset of extrinsics. +If extrinsics are batched together that require different DID keys, the `authorizeBatch` function will call the sign callback multiple times. + +## Generate and Verify a DID Signature + +In addition to being used to authorize chain operations, both light and full DIDs have off-chain applications. + +One such applications is generating digital signatures. +As a DID can have multiple keys, in addition to the signature data itself, a DID signature contains information about the signer's DID and key used, so that Verifiers have all the information needed to resolve the DID from the KILT blockchain and use the right key to verify the generated signature. + +The snippet below shows how to generate and verify a DID signature using the KILT SDK. + +```ts +const transactionHandler = Kilt.DidHelpers.createDid({ + api, + signers: [authenticationKeyPair], + submitter: submitterAccount, + fromPublicKey: authenticationKeyPair.publicKeyMultibase +}) + +const didDocumentTransactionResult = await transactionHandler.submit() +``` + +:::note +Notice that the snippet above takes a `DidDocument` instance to generate the signature. +A `DidDocument` can represent either a light or a full DID. +This means that both light and full DIDs can generate signatures, and the KILT SDK implements the right verification logic depending on whether the signer is a light or a full DID. +::: + +## web3names + +### Claim a web3name + +A web3name can be claimed if it currently has no owner, using the following snippet as reference. + +```ts +const transactionHandler = Kilt.DidHelpers.createDid({ + api, + signers: [authenticationKeyPair], + submitter: submitterAccount, + fromPublicKey: authenticationKeyPair.publicKeyMultibase +}) + +const didDocumentTransactionResult = await transactionHandler.submit() +``` + +The claiming process requires the reservation of a deposit that is freed upon web3name release. + +Once claimed, the web3name will start appearing whenever the DID of its owner is resolved, for instance via the [Universal Resolver](https://dev.uniresolver.io/#did:kilt:4pZGzLSybfMsxB1DcpFNYmnqFv5QihbFb1zuSuuATqjRQv2g). +For more information about web3names and DIDs, see the official [KILT DID Specification](https://github.com/KILTprotocol/spec-kilt-did/blob/main/README.md). + +### Query public credentials for a web3name + +web3names are linked to KILT DIDs, and KILT DIDs can define services to expose additional service/information. +One of the possible endpoint types is the [`KiltPublishedCredentialCollectionV1`][kilt-published-credential-collection-v1-type] type. +The type defines the structure to make KILT credentials public and accessible to anyone. + +Because of the relationship between web3names and DIDs, it is possible, given a certain web3name, to retrieve all public credentials that the DID subject identified by that web3name has made available. +Below is a code snippet showing how to do that using the KILT SDK, and how to perform the needed security checks/validation as recommended by the [specification][kilt-published-credential-collection-v1-type]. + + + {QueryNameCredentials} + + +[kilt-published-credential-collection-v1-type]: https://github.com/KILTprotocol/spec-KiltPublishedCredentialCollectionV1/blob/main/README.md + +### Release a web3name + +If a web3name is no longer needed, either the DID owner or the deposit payer can release it, with deposit being released and returned to the original payer. + +### Releasing a Web3name by the DID Owner + +In the case of the DID owner willing to release the web3name, the following snippet provides a reference implementation on how to achieve that. + +```ts +const transactionHandler = Kilt.DidHelpers.createDid({ + api, + signers: [authenticationKeyPair], + submitter: submitterAccount, + fromPublicKey: authenticationKeyPair.publicKeyMultibase +}) + +const didDocumentTransactionResult = await transactionHandler.submit() +``` + +In the code above, the `releaseWeb3Name` function takes the following parameters: + +- **did**: The DID URI of the owner. +- **submitterAccount**: The keyring pair of the submitter. + + +The function `releaseWeb3Name` uses the KILT SDK to create a _web3name release transaction_ using `api.tx.web3Names.releaseByOwner`. +It then authorizes the transaction using the `Kilt.Did.authorizeTx` method and submits the authorized transaction to the blockchain using `Kilt.Blockchain.signAndSubmitTx`. +This process ensures that the release transaction is signed by the DID owner. + +### Reclaiming a Web3name Deposit by the Deposit Payer + +If the web3name is being released by the deposit payer, the signature of the DID owner is not required; a regular signed extrinsic can be submitted to the KILT blockchain, as shown below. + +```ts +const transactionHandler = Kilt.DidHelpers.createDid({ + api, + signers: [authenticationKeyPair], + submitter: submitterAccount, + fromPublicKey: authenticationKeyPair.publicKeyMultibase +}) + +const didDocumentTransactionResult = await transactionHandler.submit() +``` + +In the code above, the `reclaimWeb3NameDeposit` function takes the following parameters: + +- **submitterAddress**: The keyring pair of the submitter. +- **web3Name**: The web3name for which the deposit is to be reclaimed. + +The function creates a web3name deposit reclaim transaction using `api.tx.web3Names.reclaimDeposit` and submits the signed transaction to the blockchain using `Kilt.Blockchain.signAndSubmitTx`. +Since the web3name is being released by the deposit payer, the signature of the DID owner is not required. + +By using these code examples, you can easily release or reclaim the deposit of a web3name, depending on the scenario and the role of the entity initiating the release. + +### Resolve a web3name + + + +Resolving the web3name will provide the same information as resolving a DID does. + +To query and retrieve the DID document associated with a web3name, you can use the following code example: + +```ts +const transactionHandler = Kilt.DidHelpers.createDid({ + api, + signers: [authenticationKeyPair], + submitter: submitterAccount, + fromPublicKey: authenticationKeyPair.publicKeyMultibase +}) + +const didDocumentTransactionResult = await transactionHandler.submit() +``` + +In the code example above, the `queryDidDocument` function takes a web3Name parameter, which represents the web3name to be resolved. +It internally uses the `api.call.did.queryByWeb3Name` method to query the information of the provided web3name from the blockchain. + +The function then decodes the result using `Kilt.Did.linkedInfoFromChain` to extract the associated DID document and any other linked blockchain accounts. Finally, it returns the resolved DID document.