# Asset DID and public credentials for assets (public technical draft)
**DISCLAIMER: this is a technical draft and targets a technical audience with knowledge of the KILT protocol and of the Substrate framework.**
Credentials are typically composed of two elements: an *identifier* for the subject and *information* about that subject. KILT currently supports identifiers that refer to **active** entities, i.e., user agentss or devices capable of interacting with other parties and to perform cryptographic operations.
Defining a class of identifiers that can be used with "objects", i.e., entities that are not capable of performing actions on their own (e.g., NFTs), is the scope of the first part of this document.
The second part of this document details how the current KILT infrastructure can be updated to support issuing credentials to such "objects" in a way that is transparent and visible by everyone else.
## Asset DID definition
An *asset DID* is a new (not yet drafted) DID specification that combines different [CAIPs](https://github.com/ChainAgnostic/CAIPs) (Chain-Agnostic Improvement Proposal).
Specifically, the relevant CAIPs are:
- [CAIP-2](https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-2.md): defines a way to identify a blockchain in a human readably, developer friendly and transaction-friendly way.
- [CAIP-10](https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-10.md): defines a way to identify an account in any blockchain specified by CAIP-2 blockchain id.
- [CAIP-19](https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-19.md): defines a way to identify a type of asset and an asset ID (e.g., for a non-fungible token) in a human-readable, developer and transaction friendly way.
The Asset DID would be a generalization of the [NFT DID](https://github.com/ceramicnetwork/CIP/blob/main/CIPs/CIP-94/CIP-94.md), since it would be used to identify not only NFTs, but also generic asset classes, e.g., the BTC fungible token, or an entire parachain.
### Examples of Asset DIDs
Below are some examples of the new DID method for some entity classes.
#### Dotsama chains
Any Dotsama chain could be identified by a CAIP-2 identifier, by using the first 64 bytes of the genesis hash as the unique identifier for each of them:
```
polkadot = "polkadot" + ":" + "91b171bb158e2d3848fa23a9f1c25182"
statemint = "polkadot" + ":" + "68d56f15f85d3136970ec16946040bc1"
kusama = "polkadot" + ":" + "b0a8d493285c2df73290dfb7e61f870f"
statemine = "polkadot" + ":" + "48239ef607d7928874027a43a6768920"
acala = "polkadot" + ":" + "fc41b9bd8ef8fe53d58c7ea67c794c7e"
spiritnet = "polkadot" + ":" + "411f057b9107718c9624d6aa4a3f23c1"
peregrine = "polkadot" + ":" + "a0c6e3bac382b316a68bca7141af1fba"
```
An asset DID for the Polkadot relay chain would then have the following structure:
```
did:asset:polkadot:91b171bb158e2d3848fa23a9f1c25182
```
#### ERC721 NFT collections and pieces
For ERC721 NFTs, a CAIP-19 namespace of `erc721` has already been defined, hence the CryptoKitties collection has the following CAIP-19 identifier:
```
eip155:1/erc721:06012c8cf97BEaD5deAe237070F9587f8E7A266d
```
which would result in the following Asset DID, where occurrences of `/` are replaced by `:` to adhere to the [DID Core Syntax](https://www.w3.org/TR/did-core/#did-syntax):
```
did:asset:eip155:1:erc721:06012c8cf97BEaD5deAe237070F9587f8E7A266d
```
A particular piece of NFT belonging to the collection would have the following CAIP-19 identifier:
```
eip155:1/erc721:06012c8cf97BEaD5deAe237070F9587f8E7A266d/771769
```
resulting in the following Asset DID:
```
did:asset:eip155:1:erc721:06012c8cf97BEaD5deAe237070F9587f8E7A266d:771769
```
### Asset DID resolution
Being a generative and entirely self-contained identifier, Asset DIDs do not require interaction with any storage layer to be resolved.
Given the following Asset DID
```
did:asset:eip155:1:erc721:06012c8cf97BEaD5deAe237070F9587f8E7A266d:771769
```
the key parts are parsed and interpreted
```
did:asset -> Asset DID prefix
eip155:1 -> main Ethereum blockchain
erc721 -> ERC721 NFT
06012c8cf97BEaD5deAe237070F9587f8E7A266d -> CryptoKitties collection
771769 -> CryptoKitties piece
```
## Public credential issuance and verification
Similarly to identifiers, also credentials for assets are different than traditional KILT credentials, since the subject is not an active entity.
As a refresher, the KILT credentialing infrastructure defines the following components:
- a **claim**, which contains the information about the subject that the claimer wants to get attested.
- a **request for attestation**, which contains the original claim along with additional information that enables selective disclosure and non-repudiation.
- an **attestation**, which contains the chain information about the attestation process, including the attester's identity.
- a **credential**, which is simply the union of a request for attestation and the attestation information.
Since public credentials differ from traditional KILT credentials, for which only the credential root hash is stored on chain, the only relevant element is the request for attestation, of which an example is shown below:
```json=
{
"claim": {
"cTypeHash": "0x47d04c42bdf7fdd3fc5a194bcaa367b2f4766a6b16ae3df628927656d818f420",
"contents": {
"Twitter": "mr-x"
},
"owner": "did:kilt:4pqDzaWi3w7TzYzGnQDyrasK6UnyNnW6JQvWRrq6r8HzNNGy"
},
"claimHashes": [
"0xb5984f7bf846423a4e884958d02fe479740a526d2a48f3eb28c2ebd585d79652",
"0xdd6d86383c8b70b2fb0a56ff6d91d37220a305f5b72c5b437cc2a3c34c077b0e"
],
"claimNonceMap": {
"0x23d9ca2b4e78dadf9bba99396f1023a9912dd7ca60f4346a19372c79cf71608e": "05e74568-4685-4550-ac6c-368120696634",
"0x5690599ee6cb1835146780d883b5ab5ff83a0e55fef79dc5c721c6cb125c6e22": "f9bc9b46-61c3-47f0-95ea-7cc53f374b9e"
},
"claimerSignature": {
"keyId": "did:kilt:4pqDzaWi3w7TzYzGnQDyrasK6UnyNnW6JQvWRrq6r8HzNNGy#0xfb589865a4ecd8bf5e9f9e7c7d26293d6123f9c2d09b92e0a787f9641918d6b3",
"signature": "0x0c4bb527df1d4c4ce0ac35beeecec42422cd84fdd8272dee2b2f28305c6e73594ff5b72dfad266b6aa756af161690ae96c234ba9a1bb3998c969f3d5ef4b768b"
},
"legitimations": [],
"delegationId": null,
"rootHash": "0x73b2063f713258256c93eecc6b7633583647aa9232b1ed5620eb971cd3309727"
}
```
Not all the fields from the object above are necessary in the flows below, so when referring to a `request_for_attestation`, the object is meant to have the following structure:
```json=
{
"claim": {
"cTypeHash": "0x47d04c42bdf7fdd3fc5a194bcaa367b2f4766a6b16ae3df628927656d818f420",
"contents": {
"Twitter": "mr-x"
},
"owner": "did:kilt:4pqDzaWi3w7TzYzGnQDyrasK6UnyNnW6JQvWRrq6r8HzNNGy"
},
"claimerSignature": {
"keyId": "did:kilt:4pqDzaWi3w7TzYzGnQDyrasK6UnyNnW6JQvWRrq6r8HzNNGy#0xfb589865a4ecd8bf5e9f9e7c7d26293d6123f9c2d09b92e0a787f9641918d6b3",
"signature": "0x0c4bb527df1d4c4ce0ac35beeecec42422cd84fdd8272dee2b2f28305c6e73594ff5b72dfad266b6aa756af161690ae96c234ba9a1bb3998c969f3d5ef4b768b"
},
"nonce": "05e74568-4685-4550-ac6c-368120696634",
"legitimations": [],
"delegationId": null,
"rootHash": "0x73b2063f713258256c93eecc6b7633583647aa9232b1ed5620eb971cd3309727"
}
```
Specifically:
- `claimHashes` was removed as it can be re-computed by using the provided `nonce` for all the claims in `claim.contents`.
- `claimNonceMap` was replaced with a single `nonce` value, since the same nonce is to be applied to all the claims in `claim.contents` to arrive to the same values as what used to be the `claimHashes` array.
### Issuance flows
We have envisioned two different flows for credential issuance, supporting two different but not entirely separate classes of use cases:
1. an attester issuing a public credential to an asset spontaneously, i.e., without being directly requested to do so by any claimer
2. an attester issuing a public credential to an asset on request by a claimer.
The rest of the document will refer to **blockchain state** as the trie-based structure that represents the state of the blockchain storage at any point in time and which all validators have to reach consensus about. On the other hand, the term **blockchain history** refers to the information about all the blocks that have been produced since genesis, and which can be retrieved from full nodes via the RPC calls `state > getBlockHash(blockNumber)` and `state > getBlock(hash)`.
### Use case 1: attester-initiated credential issuance
This use case is for attesters to spontaneously issue attestations to assets, without any claimer's involvement.
#### Issuance steps
1. The *attester* creates a `claim` object for the asset, including selecting a CType for the claim.
2. The *attester* creates a `request_for_attestation` object (example shown above) by adding any `legitimations` they want to make public in the attestation process, an optional `delegationId`, hashing each claim with a single randomly-generated *nonce* (the same for all claims) and computing the root hash for the request according to the KILT specification.
- Since no claimer is involved in this use case, the object will not have any `claimerSignature` property.
- Nonces are used for selective disclosure of claims within a credential. In this case, credentials are meant to be public and accessible as a whole, hence selective disclosure is not needed. To save space on what is added to the blockchain, by defining that each claim is hashed with the same attester-chosen nonce, we save on storage space and we make it possible for anyone to re-compute the root hash of an attestation given only the claim object, the list of legitimations, the optional delegation ID, and the single nonce value to be used to hash all claims.
3. The *attester* submits a `issue_public_credential(request_for_attestation)` transaction to the KILT blockchain.
- The maximum size of the `request_for_attestation` object will be *limited to something around 10-100 Kbs* to reduce block congestion.
- Credentials that need more space (e.g., embedding an image or even a video), could study alternative approaches, including hosting the actual resource on some third-party service and specify only its URI in the claim. Measures should be taken by application developers to avoid that the resource behind the URI does not change over time, e.g., by using cryptographically-verifiable links or CIDs.
- Since `claim.contents` might contain nested structures, the blockchain will expect this object to be serialized as a `Vec<u8>`, i.e., a byte array. The details of how this is done will be defined in the near future, e.g., whether consumers of credentials are expected to deserialize it from a more concise format (e.g., CBOR) to the original KILT object, which is a JSON object. Additionally, the runtime cannot perform any sort of JSON schema validation, since that would require fetching additional resources from third-party services. Hence, all the validation logic will have to be performed by the consumers/verifiers.
4. The blockchain parses the `request_for_attestation` object and extracts the necessary information from it.
- Takes `claim.cTypeHash` and verifies that the CType hash already exists on chain.
- Takes `rootHash` and verifies that it does not already refer to a previously-attested credential.
- Takes `legitimations` and verifies that each of them refers to an attestation already on chain.
- Takes `delegationId`, if present, and verifies that a delegation node with that ID and with attestation permissions exists on chain.
5. Unless the claim is too large, the tx is included in the block regardless of the result of the checks performed in the step above.
- A *negative* result will not change the blockchain state, meaning there will not be a reference from the state to the block in which the tx was included, and consumers will treat the credential in the tx as not being valid.
- A *positive* result, on the other hand, will result in the block number in which the tx is included being added to the blockchain state under the credential subject specified in `claims.owner` for efficient lookup in the blockchain history. This spares us the necessity to store the whole claim in the blockchain state, but gives consumers the chance to efficiently retrieve it from the blockchain history using the block number information stored in the blockchain state and the RPC endpoints `state > getBlockHash(blockNumber)` and `state > getBlock(hash)` (see verification steps below for more information).
At the end of the issuance process, the following information is added on chain:
- In the [`attestation`](https://github.com/KILTprotocol/kilt-node/tree/master/pallets/attestation) pallet, the credential `rootHash` is added along with the information about the `claim.cTypeHash`, and the `delegationId`, as is the case with traditional KILT credentials.
- In the `public_credential` pallet, a new entry for `claim.owner -> rootHash -> block_number` is added.
- *Whether this information would live in a new pallet or in the current attestation pallet has not been decided yet* and will have to undergo further research to decide which way would be more efficient.
#### Verification steps
Verifiers can either fetch all the credentials issued to a given asset, or can simply retrieve a single credential, given its root hash. The current design does not support filtering credentials matching a specific CType; that would have to take place on the client application side.
Given an Asset DID, a verifier must perform the following steps for each of the public credentials stored on the KILT blockchain under that identifier:
1. Retrieve the block in which the tx was included, by calling `state > getBlockHash(blockNumber)` and `state > getBlock(hash)`.
- We will most likely expose a custom RPC endpoint in the future that would combine both calls and would perform additional validation checks.
2. Identify in the block the tx corresponding to the creation of the attestation. For that, the information about the root hash can be used in case of multiple operations of the same type within the same block.
3. Parse the tx to extract the parameters of the call.
- Given the encoded parameters, the client would have to perform a deserialization step depending on how `claim.contents` is expected to be serialized.
- The client then has to hash each property of `claim.contents` with the `nonce` value.
- Given the hashed claims, the optional `delegationId` and the `legitimations`, the root hash must be computed. This function is provided by the KILT SDK.
4. Once the original `request_for_attestation` object has been re-created, use the KILT SDK to validate its structure and its data.
- This step must include verifying that the credential `rootHash` matches the value as it is stored on chain for the credential. It would be possible for an attester to encode a claim and store it under an existing different root hash, since the chain has no way to check that. When verifying the credential with the SDK, it would be mistakenly verified successfully, because the SDK has no context to make this sort of judgement.
5. Verify that the `claim.owner` matches the identifier of the asset under consideration.
6. Verify that the attestation under the root hash has not been revoked/removed.
7. Verify that any delegations included in the claim exist and have not been revoked/removed.
### Use case 2: claimer-requested credential issuance
This use case is for attesters to issue attestations to assets upon request by a claimer, who is interested in getting the asset certified by the attester in question.
The differences in both the issuance and the verification steps compared to use case 1 are **highlighted in bolt** for quicker consultation.
#### Issuance steps
1. The ***claimer*** creates a `claim` object for the asset, including selecting a CType for the claim.
2. The ***claimer*** creates a `request_for_attestation` object (example shown above) by adding any `legitimations` they want the attester to fulfil in the attestation process, an optional `delegationId`, hashing each claim with a single randomly-generated *nonce* (the same for all claims) and computing the root hash for the request according to the KILT specification.
- Nonces are used for selective disclosure of claims within a credential. In this case, credentials are meant to be public and accessible as a whole, hence selective disclosure is not needed. To save space on what is added to the blockchain, by defining that each claim is hashed with the same **claimer**-chosen nonce, we save on storage space and we make it possible for anyone to re-compute the root hash of an attestation given only the claim object, the list of legitimations, the optional delegation ID, and the single nonce value to be used to hash all claims.
- **In this case, since the claimer is involved in the use case, the object will have a `claimerSignature` property being a signature over the computed `rootHash`.**
- **How the `request_for_attestation` is sent to the attester is out of scope of this document.**
3. The *attester* submits a `issue_public_credential(request_for_attestation)` transaction to the KILT blockchain.
- The maximum size of the `request_for_attestation` object will be *limited to something around 10-100 Kbs* to reduce block congestion.
- Credentials that need more space (e.g., embedding an image or even a video), could study alternative approaches, including hosting the actual resource on some third-party service and specify only its URI in the claim. Measures should be taken by application developers to avoid that the resource behind the URI does not change over time, e.g., by using cryptographically-verifiable links or CIDs.
- Since `claim.contents` might contain nested structures, the blockchain will expect this object to be serialized as a `Vec<u8>`, i.e., a byte array. The details of how this is done will be defined in the near future, e.g., whether consumers of credentials are expected to deserialize it from a more concise format (e.g., CBOR) to the original KILT object, which is a JSON object. Additionally, the runtime cannot perform any sort of JSON schema validation, since that would require fetching additional resources from third-party services. Hence, all the validation logic will have to be performed by the consumers/verifiers.
4. The blockchain parses the `request_for_attestation` object and extracts the necessary information from it.
- Takes `claim.cTypeHash` and verifies that the CType hash already exists on chain.
- Takes `rootHash` and verifies that it does not already refer to a previously-attested credential.
- Takes `legitimations` and verifies that each of them refer to an attestation already on chain.
- Takes `delegationId`, if present, and verifies that a delegation node with that ID and with attestation permissions exists on chain.
- **Takes `claimerSignature` and verifies it against the provided `rootHash` value**.
5. Unless the claim is too large, the tx is included in the block regardless of the result of the checks performed in the step above.
- A *negative* result will not change the blockchain state, meaning there will not be a reference from the state to the block in which the tx was included, and consumers will treat the credential in the tx as not being valid.
- A *positive* result, on the other hand, will result in the block number in which the tx is included being added **and the claimer signature** to the blockchain state under the credential subject specified in `claims.owner` for efficient lookup in the blockchain history. This spares us the necessity to store the whole claim in the blockchain state, but gives consumers the chance to efficiently retrieve it from the blockchain history using the block number information stored in the blockchain state and the RPC endpoints `state > getBlockHash(blockNumber)` and `state > getBlock(hash)` (see verification steps below for more information).
At the end of the issuance process, the following information is added on chain:
- In the [`attestation`](https://github.com/KILTprotocol/kilt-node/tree/master/pallets/attestation) pallet, the credential `rootHash` is added along with the information about the `claim.cTypeHash`, and the `delegationId`, as is the case with traditional KILT credentials.
- In the `public_credential` pallet, a new entry for `claim.owner -> rootHash -> block_number` is added.
- *Whether this information would live in a new pallet or in the current attestation pallet has not been decided yet* and will have to undergo further research to decide which way would be more efficient and scalable.
#### Verification steps
Verification follows the same steps as the previous use case.
**Once an MVP of the new feature is developed, we will provide instructions and example code snippets using the KILT SDK on how to perform issuance and verification for both use cases, including any support utility functions.**
## Touchpoints with XCM v3
The concept of asset DID (and asset identifier) partially overlaps with the concepts of `MultiLocation` and `MultiAsset` as defined in XCM v3, as our goal is to include in this identifier class both chains (locations) and assets. By having a collection of structured types for the different asset identifiers, it should be fairly easy to implement an adapter for most of the `MultiLocation` and `MultiAsset` variants.
## Changelog
- **May.23 2022**: Initial version.
- **May.27 2022**:
- Change well-known nonce from `0x0000000000000000000000000000000000000000000000000000000000000000` to an attester or claimer -specified value to allow the same claim to be attested more than once by different attesters.
- Specify the structure of the `request_for_attestation` object that is part of the extrinsic call.