🟢ADR 002: DID-Linked Resources

Status

Category
Status

Authors

Ankur Banerjee, Alexandr Kolesov, Alex Tweeddale, Renata Toktar

ADR Stage

ACCEPTED

Implementation Status

Implemented

Start Date

2021-09-23

Last Updated

2023-02-06

Summary

This ADR defines how resources (e.g., text, JSON, images, etc) can be created and referenced using a persistent and unique did:cheqd DID URL.

Each resource will be linked with a DID Document, with create/update operations controlled using the specified verification methods in the associated DID Document.

Context

"Resources" in decentralised identity

Trust over IP Foundation (ToIP) describes how "resources" could be generically defined and accessed using DID URLs. In a self-sovereign identity (SSI) ecosystem, such resources are often required in tandem with W3C Verifiable Credentials, which is a standard way of representing portable digital credentials that represent claims about its subjects and can be verified via digital proofs.

Common types of resources that might be required to issue and validate Verifiable Credentials are:

Figure 1: Mobile boarding passes in Apple Wallet showing different visual styles (source British Airways Media centre

Such visual representations can also be used to quickly communicate information visually during identity exchanges, such as airline mobile boarding passes. In the example above from British Airways, the pass at the front is for a "Gold" loyalty status member, whereas the pass at the back is for a "standard" loyalty status member. This information can be represented in a Verifiable Credential, of course, but the example here uses the Apple Wallet / Google Wallet formats to overlay a richer, "human-friendly" display.

More broadly, there are other resources that might be relevant for issuers and verifiers in a self-sovereign identity exchange:

Rationale for storing resources on-ledger

Decentralized Identifiers (DIDs) are often stored on ledgers (e.g., cheqd, Hyperledger Indy), distributed storage (e.g., IPFS in Sidetree), or non-ledger distributed systems (e.g., KERI).

Drawbacks of hosting resources on traditional web endpoints

DIDs can be stored on traditional centralised-storage endpoints (e.g., did:web, did:git) but this comes with certain drawbacks:

  1. DIDs could be tampered by compromising the hosting provider: DIDs and DID Documents ("DIDDocs") stored at a centralised web endpoint can be compromised and replaced by malicious actors.

  2. Hosting providers could unilaterally cease to host particular clients: Hosting providers could terminate accounts due to factors such as non-payment of fees, violation of Terms of Service, etc.

  3. Single point-of-failure in resiliency: Even for highly-trusted and sophisticated hosting providers who may not present a risk of infrastructure being compromised, a service outage at the hosting provider can make a DID anchored on their systems inaccessible.

  4. Link rot: "Link rot" happens when over time, URLs become inaccessible, either because the endpoint where the content was stored is no longer active, or the URL format itself changes. The graph below from an analysis by The New York Times of linkrot shows degradation over time of URLs.

Risks applicable in the context of Verifiable Credentials

The issues highlighted above a material difference to the longevity of Verifiable Credentials.

For example, a passport (which typically have a 5-10 year validity issued as a Verifiable Credential anchored to a DID (regardless of whether the DID was on-ledger or not) might stop working if the credential schema, visual presentation format, or other necessary resources were stored off-ledger on traditional centralised storage.

Despite these issues, many self-sovereign identity (SSI) implementations - even ones that use ledgers / distributed systems for DIDs - often utilise centralised storage. From the W3C Verifiable Credential Implementation Guide:

Example schema.org address with full URLs

{
  "@type": "http://schema.org/Person",
  "http://schema.org/address": {
    "@type": "http://schema.org/PostalAddress",
    "http://schema.org/streetAddress": "123 Main St.",
    "http://schema.org/addressLocality": "Blacksburg",
    "http://schema.org/addressRegion": "VA",
    "http://schema.org/postalCode": "24060",
    "http://schema.org/addressCountry": "US"
  }
}

Using traditional web endpoints to store resources (such as schemas) that are critical for a Verifiable Credential to function undermines the benefits that persistently-accessible Decentralized Identifiers offer.

Design principles

We took the following design principles into consideration, along with an explanation of how we addressed them:

  1. Built using existing, familiar DID Core Spec patterns: Wherever possible, our design attempts to utilise existing patterns and behaviours within the W3C DID Core specification (such as the use of DID URLs to identify resources), instead of trying to implement proprietary/custom approaches. We believe that similar designs could be adopted by other DID methods if they choose.

  2. Protection against linkrot for long-term retrieval: Any Resource stored on-ledger is replicated across multiple nodes.

    1. If any individual node or endpoint is down, lookup requests can be sent to any other node on the network.

    2. In a catastrophic scenario where the network itself stops to exist, e.g., companies shutting down, getting acquired etc the on-ledger data can still be restored by digital archivists using ledger snapshots. A practical example of this is how Cosmos Hub makes historical chain archives available which can be restored. While this can be cumbersome, we wanted to design for this as a fail-safe.

  3. Extensible by default: Our objective was to build a flexible design pattern that allowed developers to define and extend their own resource types. Trying to control what kinds of resources could be written to ledger would make the ledger-side logic complex. Instead, we opted for a design where the cheqd ledger acts agnostically to store resources, as long as correctly authorised, as a permanently-accessible endpoint.

  4. Design for DID-spec "dumb" as well as DID-spec "smart" client applications: Many approaches in this space assume that client applications must be adept at parsing DIDDocs and resolving complex inter-DIDDoc relationships. We saw describing resources using DIDDocs as metadata about the resource which could be independently-parsed by "smart" client applications; while also providing a fallback approach for "dumb" client applications. We internally considered this as "What if an identity wallet understood how to parse JSON, but didn't understand the DID Core spec?"

  5. Version controlled: The ability to evolve a resource over time is critical for identity use cases. As described above, examples of this include when identity document schemas change, logos evolve, etc. Current approaches (such as Hyperledger Indy CredDefs) deal with this by creating entirely new, unlinked resources. We designed to make it easy, using existing DID Core specification techniques, so that client applications could query "What was the version of a resource with this name on this date/time?"

  6. Make re-use of resources simple: We liked the concept of Schema.org in the sense that it promotes a common, machine-readable specification. Therefore, our design allows for patterns where the controllers of a DID can reference resources created by other DID owners/controllers, e.g., referencing a pre-existing schema. Taking this line of thought further, it allows for an arbitrary depth of how resources can be nested, as long as they are discoverable/resolvable.

  7. Notalltypes of resources should be stored on a ledger...but can be made discoverable through similar design patterns: Distributed ledgers are great for redundancy, but the cost of this duplication (in terms of storage required by node, block size, transaction finality speeds, etc) can be quite significant. For instance, a distributed ledger is probably not the best storage and retrieval mechanism for a video file (which can run into many GBs/TBs); or even a PDF larger than a few MBs. cheqd network restricts the block size for an individual block to ~200 KB. This can be updated via an on-ledger vote, but the trade-off of asking node operators to provision ever-expanding storage would be not ideal. Our design therefore restricts the file/payload size of on-ledger resources (in our case, ~190 KB - giving enough room for transaction data besides the resource itself), while allowing the same techniques below to be used for describing off-ledger resources. E.g., our first DID on cheqd network references a 7+ MB image accessible via IPFS. We recognise and accept that DID owners/creators may choose to use their own centralised/decentralised storage, and the design patterns described below accommodate that.

Resources on cheqd ledger

Resources on cheqd ledger are collated under Resource Collections, which are defined as a list of resources linked to and controlled using a DID Document ("DIDDoc").

Figure 4: Overview of Resource and Resource Collection creation editable version)

To create a new Resource, a client application first needs to create a DID (or use an existing not deactivated DID along with its associated DIDDoc.) This DID-Linked Resource is the lowest, direct level of create/update/deactivate operation control that exists.

Individual Resources are uniquely identified by a common Resource Name and common Resource Type that MUST remain consistent across versions. The specific version number of a Resource is described using the Resource ID, which is a Universally-Unique Identifier (UUID). Since UUIDs can be generated by any compatible software library, client applications are able to define this version number independent of the cheqd ledger. This same technique and rationale is described in ADR-001: cheqd DID method.

This allows a specific Resource version to be referenced in a Verifiable Credential, as well as allowing client applications to query historical/updated Resource versions along with metadata that describes how the Resource evolved within a Resource Collection.

Discoverability via DIDDoc Metadata

Once a Resource has been created under a Resource Collection, the parent DIDDoc will automatically have an updated didDocumentMetadata section, including linkedResourceMetadata.

The syntax of the linked Resource metadata is as follows:

"didDocumentMetadata": {
    "created": "2020-12-20T19:17:47Z",
    "updated": "",
    "deactivated": false,
    "versionId": "bdab59b0-66f5-42d3-b809-1829bdcc0408",
    "previousVersion": "",
    "nextVersion": "",
    "linkedResourceMetadata": [
      { // First version of a Resource called PassportSchema
        "resourceURI": "did:cheqd:testnet:13d5ad44-9e99-428f-81e9-274458cefddc/resources/44547089-170b-4f5a-bcbc-06e46e0089e4",
        "resourceCollectionId": "13d5ad44-9e99-428f-81e9-274458cefddc", // Common collection ID
        "resourceId": "44547089-170b-4f5a-bcbc-06e46e0089e4", // Old Resource ID and version number
        "resourceName": "PassportSchema", // Resource name must remain the same
        "resourceType": "CL-Schema", // Resource type must remain the same
        "resourceVersion": "1.0.1", // A user-set version
        "mediaType": "application/json",
        "created": "2022-07-19T08:40:00Z",
        "checksum": "7b2022636f6e74656e74223a202274657374206461746122207d0ae3b0c44298", // Old version checksum
        "previousVersionId": "", // empty string, since no previous version
        "nextVersionId": "bb2118f3-5e55-4510-b420-33ef9e1726d2", // Points to next version below
        },
      { // Second version of a Resource called PassportSchema
        "resourceURI": "did:cheqd:testnet:13d5ad44-9e99-428f-81e9-274458cefddc/resources/bb2118f3-5e55-4510-b420-33ef9e1726d2",
        "resourceCollectionId": "DAzMQo4MDMxCjgwM", // Common collection ID
        "resourceId": "bb2118f3-5e55-4510-b420-33ef9e1726d2", // New Resource ID and version number
        "resourceName": "PassportSchema", // Resource name must remain the same
        "resourceType": "CL-Schema", // Resource type must remain the same
        "resourceVersion" "1.0.1" // user-set semantic version control
        "mediaType": "application/json",
        "created": "2022-08-07T08:40:00Z",
        "checksum": "9123dcbb0b42652b0e105956c68d3ca2ff34584f324fa41a29aedd32b883e131", // New version checksum
        "previousVersionId": "44547089-170b-4f5a-bcbc-06e46e0089e4", // Points to previous version above
        "nextVersionId": "0be87654-4a48-4f8e-8789-15ec3589ccdd" // Points to next version. Empty string if no new version
        }
      ]
    }

Importantly, we decided not to populate the actual resource data into the didDocumentMetadata, but instead, what we refer to as a Resource Preview which contains all the metadata about the associated resources.

Resource Preview

Resource previews will aopear within DIDDocMetadata. These do not include the actual core data of the resource and only reference the metadata:

  • Resource Collection ID: (did:cheqd:...:) (supplied client-side)**

  • Resource ID: UUID ➝ specific to resource, also effectively a version number (supplied client-side)

  • Resource Name: String (e.g., CL-Schema1 (supplied client-side))

  • Resource Type (supplied client-side)

  • Resource Version (supplied client-side)

  • MediaType: (e.g. application/json/image/application/octet-stream/text/plain) (computed ledger-side)

  • Created: XMLDatetime (computed ledger-side)

  • Checksum: SHA-256 (computed ledger-side)

  • previousVersionId: empty string if first, otherwise ID as long as Name, ResourceType, and MimeType match previous version (computed ledger-side)

  • nextVersionId: empty string if first/latest, otherwise ID as long as Name, ResourceType, and MimeType match previous version (computed ledger-side)

  • Also known as: a list of alternative URIs that can be used to get the resource.

Example:

{
  "resourceUri": "did:cheqd:testnet:13d5ad44-9e99-428f-81e9-274458cefddc/resources/bb2118f3-5e55-4510-b420-33ef9e1726d2",
  "resourceCollectionId": "13d5ad44-9e99-428f-81e9-274458cefddc",
  "resourceId": "bb2118f3-5e55-4510-b420-33ef9e1726d2",
  "resourceName": "PassportSchema",
  "resourceType": "CL-SChema",
  "resourceVersion": "1.0.1",
  "mediaType": "application/json",
  "created": "2022-04-20T20:19:19Z",
  "checksum": "a7c369ee9da8b25a2d6e93973fa8ca939b75abb6c39799d879a929ebea1adc0a",
  "previousVersionId": "67f2df00-0b6e-404b-8c70-1d63200e6412",
  "nextVersionId": "98922424-c214-4439-b52c-f68ddb450b40",
  "alsoKnownAs": [{
      "uri": "https://example.com/alternative-uri",
      "description": "Alternative URI description"
      },
      {
      "uri": "https://example.com/alternative-uri",
      "description": "Alternative URI description"
      }]
}

Optional Discoverability via DIDDoc Services

Once a Resource has been created under a Resource Collection, the linked DIDDoc can be updated to provide a link to access it in the service section.

The rationale for linking to Resources in this manner, instead of creating a new top-level section, are as follows:

  1. Client applications capable of doing DID Resolution may have strong architectural assumptions to only expect the default DID Core specification sections in a response. We considered the possibility that such applications might (incorrectly) reject the entire DIDDoc as malformed, or crash in the process of trying to parse the DIDDoc.

  2. On the other hand, the Service section in a DIDDoc is designed to be flexible and extensible by design. New DID Service types can be registered through DID Specification Registries by anyone. We suggest a new service type called LinkedResource should be used to reference any resource on cheqd within the service section. This is conceptually similar to the existing LinkedDomains.

  3. In practice, we noted that client applications capable of DID Resolution will gracefully fail/ignore unknown Service types. Client applications that do understand a particular Service type can continue parsing/resolving content they are designed to handle.

  4. DIDDocs can reference other DIDDocs, such as when the DID Controller in one DIDDoc is specified as a Verification Method in another DIDDoc. These links can be traversed using DID URL dereferencing.

  5. Historical versions of Resources can always be accessed by traversing forwards/backwards in the Resource Collection by checking if a particular Service ID has old/new versions.

  6. Multi-party control on Resource Collection updates is possible, since DIDs with multiple controllers specified in them already handle this scenario. In the normal process of updating a DIDDoc with multiple controllers, rules can be defined by client applications and/or the ledger on whether all controllers sign an update, or whether an m-of-n threshold needs to be applied. (Currently, the cheqd ledger requires all controllers to sign off on updates.)

  7. Since the cheqd ledger does not co-relate the on-ledger cheqd/Cosmos accounts to keys that control DIDDocs, this provides another layer of access control by allowing DIDDoc controllers to rotate keys, if required.

Example of referencing a resource using the service section:

  {
    "service": [{
      "id":"did:cheqd:testnet:13d5ad44-9e99-428f-81e9-274458cefddc#PassportSchema",
      "type": "LinkedResource",
      "serviceEndpoint": [
        "https://resolver.cheqd.net/1.0/identifiers/did:cheqd:testnet:13d5ad44-9e99-428f-81e9-274458cefddc/resources/bb2118f3-5e55-4510-b420-33ef9e1726d2"
      ]
    }]
  }

Creating a new Resource within a Resource Collection

To create a new DID-Linked Resource, a client application first needs to create a DID (or use an existing DID along with its associated DIDDoc). This DID-Linked Resource is the lowest, direct level of create/update/deactivate operation control that exits.

Figure 5: Detailed sequence diagram of Resource creation on cheqd editable version.

Resources must be under the maximum block size restrictions to be able to fit into a transaction. Currently this is estimated to be ~190 KB on cheqd mainnet, based on the ~200 KB block size limit plus additional headroom for metadata that needs to be described in the ResourceHeader.

Resource creation

Each request to create a Resource must provide the following parameters, supplied by the client application:

  • Resource Collection ID: (did:cheqd:...:) (supplied client-side)

  • Resource ID: UUID ➝ specific to resource, also effectively a version number (supplied client-side)

  • Resource Name: String (e.g., CL-Schema1 (supplied client-side))

  • Resource Type: (e.g JSONSchema 2020, supplied client-side. It is recommended that new Resource Types are included in the DID Spec Registries)

  • Resource Version: String (OPTIONAL). It is a human-readable semantic version for the Resource (e.g., 1.0.0 (supplied client-side))

  • alsoKnownAs: (OPTIONAL) a list of alternative URIs that can be used to get the resource.

In addition to the above client-provided parameters, the ledger-side code will populate the following additional header fields (for properly-authenticated requests):

  • MediaType: (e.g. application/json/image/application/octet-stream/text/plain) (computed ledger-side) This is based on the file extension of the associated resource file.

  • Created: XMLDatetime (computed ledger-side)

  • Checksum: SHA-256 (computed ledger-side)

  • previousVersionId: an empty string if first, otherwise ID as long as Name, ResourceType, and MimeType match previous version (computed ledger-side)

  • nextVersionId: an empty string if first/latest, otherwise ID as long as Name, ResourceType, and MimeType match previous version (computed ledger-side)

Example using the Veramo CLI:

{
    "kms": "local",
    "payload": { // example of resource header
        "collectionId": "13d5ad44-9e99-428f-81e9-274458cefddc",
        "id": "bb2118f3-5e55-4510-b420-33ef9e1726d2",
        "name": "PassportSchema",
        "version": "1.0.1",
        "resourceType": "CL-Schema",
        "alsoKnownAs": [{
            "uri": "https://example.com/alternative-uri",
            "description": "Alternative URI description"
            },
            {
            "uri": "https://example.com/alternative-uri",
            "description": "Alternative URI description"
            }],
        "data": "eyJhdHRyIjpbIm5hbWUiLCJhZ2UiXX0=" // file with resource encoded into base64 or path to file
    },
    "signInfo": [{
        "verificationMethodId": "did:cheqd:testnet:13d5ad44-9e99-428f-81e9-274458cefddc#key-1",
        "signature": "0f5c124886178037952e87e0cdc55d185732577fca19ae877e64ac9ab24a0cc534e5326e70f1a42d785d93048aee806c359ec75a7b06f39253befd1746708438"
    }]
}

Resource inputs and outputs

This section will delineate between expected inputs in JSON and how the cheqd ledger stores the resource input in protobuf

MsgCreateResource

  • collectionId: (did:cheqd:...:)<identifier> (supplied client-side) ➝ unique identifier from DIDDoc.

  • id: UUID representing resource ID ➝ specific to resource, also effectively a version number (supplied client-side)

  • name: String (e.g., CL-Schema1 (supplied client-side))

  • version: String (OPTIONAL) ➝ a human-readable semantic version for the Resource (e.g., 1.0.0 (supplied client-side))

  • resourceType: a string representing type of resource (supplied client-side). It is recommended that new Resource Types are included in the DID Spec Registries.

  • data: Bytes representing a user data (supplied client-side)

  • alsoKnownAs (supplied client-side) a list of alternative URIs for the SAME Resource

  • signInputs: Signatures of the corresponding DID Document's controller(s) (supplied client-side).

"Example"
{
    "payload": {
        "data": "eyJhdHRyIjpbIm5hbWUiLCJhZ2UiXX0=",
        "collectionId": "91e5f0cf-5f1e-5c19-97d3-d313e84033b4",
        "id": "54cb8b4d-af33-4606-bc54-0f035ee30e0f",
        "name": "PassportScheme",
        "version": "1.0",
        "resourceType": "CL-Schema",
        "alsoKnownAs": [
            {
                "uri": "https://example.com/alternative-uri",
                "description": ""
            },
            {
                "uri": "https://example.com/alternative-uri",
                "description": "Alternative URI description"
            }
        ]
    },
    "SignInfo": [
        {
            "verificationMethodID": "did:cheqd:testnet:91e5f0cf-5f1e-5c19-97d3-d313e84033b4#key-1",
            "Signature": "m0ZE4x5Qxs+6HEBoXDrjtGTLr9GuXjIttSznoVumRWIA3GMeDipXnCVgdZfa0PUVVdr2DQy9a0NyPXPeQcXdCw=="
        }
    ]
}

MsgCreateResourceResponse

  • Resource is stored on-ledger in protobuf. This is converted back to JSON client side.

"Example"
{
    "resource": {
        "collection_id": "91e5f0cf-5f1e-5c19-97d3-d313e84033b4",
        "id": "54cb8b4d-af33-4606-bc54-0f035ee30e0f",
        "name": "PassportScheme",
        "version": "1.0",
        "resource_type": "CL-Schema",
        "also_known_as": [
            {
                "uri": "https://example.com/alternative-uri",
                "description": ""
            },
            {
                "uri": "https://example.com/alternative-uri",
                "description": "Alternative URI description"
            }
        ]
    }
}

QueryCollectionResourcesRequest

  • Collection ID: String - an identifier of linked DIDDoc.

"Example"
{ "collectionId": "91e5f0cf-5f1e-5c19-97d3-d313e84033b4" }

QueryCollectionResourcesResponse

  • Returns collection of resources created by the specific collection ID. This is converted to JSON on the client side.

"Example"
{
    "resources": [
        {
            "collection_id": "91e5f0cf-5f1e-5c19-97d3-d313e84033b4",
            "id": "54cb8b4d-af33-4606-bc54-0f035ee30e0f",
            "name": "TicketSchema",
            "version": "1.0",
            "resource_type": "CL-Schema",
            "also_known_as": [
                {
                    "uri": "https://example.com/alternative-uri",
                    "description": ""
                }
            ],
            "media_type": "application/json",
            "created": "2023-01-21T20:02:55.664985039Z",
            "checksum": "a7cd6c222ea5fc1463c0ca3f70b93035196c8c4f34d89181ff5086bd7b58bfff",
            "previous_version_id": "",
            "next_version_id": ""
        },
        {
            "collection_id": "91e5f0cf-5f1e-5c19-97d3-d313e84033b4",
            "id": "54cb8b4d-af33-4606-bc54-0f035ee30e0f",
            "name": "PassportSchema",
            "version": "1.0",
            "resource_type": "CL-Schema",
            "also_known_as": [
                {
                    "uri": "https://example.com/alternative-uri",
                    "description": ""
                },
                {
                    "uri": "https://example.com/alternative-uri",
                    "description": "Alternative URI description"
                }
            ],
            "media_type": "application/json",
            "created": "2023-01-21T20:02:55.664985039Z",
            "checksum": "a7cd6c222ea5fc1463c0ca3f70b93035196c8c4f34d89181ff5086bd7b58bfff",
            "previous_version_id": "",
            "next_version_id": ""
        }
    ]
}

QueryResourceRequest

  • Collection ID: String - an identifier of linked DIDDoc

  • ID: String - unique resource id

"Example"
{ 
  "collectionId": "91e5f0cf-5f1e-5c19-97d3-d313e84033b4",
  "id": "54cb8b4d-af33-4606-bc54-0f035ee30e0f"
}

QueryResourceResponse

  • Returns Resource with a given collection ID and ID. This is converted to JSON client side.

"Example"
{
    "resource": {
        "resource": {
            "data": "eyJ0ZXN0IjogInRlc3QifQ=="
        },
        "metadata": {
            "collection_id": "91e5f0cf-5f1e-5c19-97d3-d313e84033b4",
            "id": "54cb8b4d-af33-4606-bc54-0f035ee30e0f",
            "name": "PassportSchema",
            "version": "1.0",
            "resource_type": "CL-Schema",
            "also_known_as": [
                {
                    "uri": "https://example.com/alternative-uri",
                    "description": ""
                },
                {
                    "uri": "https://example.com/alternative-uri",
                    "description": "Alternative URI description"
                }
            ],
            "media_type": "application/json",
            "created": "2023-01-21T20:02:55.664985039Z",
            "checksum": "a7cd6c222ea5fc1463c0ca3f70b93035196c8c4f34d89181ff5086bd7b58bfff",
            "previous_version_id": "",
            "next_version_id": ""
        }
    }
}

QueryResourceMetadataRequest

  • Collection ID: String - an identifier of linked DIDDoc

  • ID: String - unique resource id

"Example"
{ 
  "collectionId": "91e5f0cf-5f1e-5c19-97d3-d313e84033b4",
  "id": "54cb8b4d-af33-4606-bc54-0f035ee30e0f"
}

QueryResourceMetadataResponse

Returns resource's metadata with a given collection ID and ID.

"Example"
{
    "resource": {
        "collection_id": "91e5f0cf-5f1e-5c19-97d3-d313e84033b4",
        "id": "54cb8b4d-af33-4606-bc54-0f035ee30e0f",
        "name": "PassportSchema",
        "version": "1.0",
        "resource_type": "CL-Schema",
        "also_known_as": [
            {
                "uri": "https://example.com/alternative-uri",
                "description": ""
            },
            {
                "uri": "https://example.com/alternative-uri",
                "description": "Alternative URI description"
            }
        ],
        "media_type": "application/json",
        "created": "2023-01-21T20:02:55.664985039Z",
        "checksum": "a7cd6c222ea5fc1463c0ca3f70b93035196c8c4f34d89181ff5086bd7b58bfff",
        "previous_version_id": "",
        "next_version_id": ""
    }
}

State

Metadata

  • resources-metadata:<collection-id>:<resource-id>Metadata

    • <collection-id> is the last part of DID. It can be UUID, Indy-style or whatever is allowed by ledger. It allows us to evolve over time more easily.

    • <resource-id> is a unique resource identifier on UUID format

Data

  • resources-data:<collection-id>:<resource-id>Data

    • <collection-id> is the last part of DID. It can be UUID, Indy-style or whatever is allowed by ledger. It allows us to evolve over time more easily.

    • <resource-id> is a unique resource identifier on UUID format

Transactions

CreateResource

  • Processing logic:

    • Check that associated DIDDoc exists;

    • Authenticate request the same way as DIDDoc creation and updating;

    • Validate properties;

    • Validate that ID is unique;

    • Set created date time;

    • Set previousVersion and nextVersion if this is a new version (a resource with the same collection-id, resource-name and resource-type exists);

    • Compute checksum;

    • Persist the resource in state;

cheqd Cosmos CLI Example:

cheqd-noded tx resource create [payload-file] [resource-data-file]
  • payload-file: path to the payload file:

{
    "payload": {
        "collectionId": "91e5f0cf-5f1e-5c19-97d3-d313e84033b4",
        "id": "54cb8b4d-af33-4606-bc54-0f035ee30e0f",
        "name": "PassportSchema",
        "version": "1.0",
        "resourceType": "CL-Schema",
        "alsoKnownAs": [
            {
                "uri": "https://example.com/alternative-uri",
                "description": ""
            },
            {
                "uri": "https://example.com/alternative-uri",
                "description": "Alternative URI description"
            }
        ]
    },
    "signInputs": [
        {
            "verificationMethodID": "did:cheqd:testnet:91e5f0cf-5f1e-5c19-97d3-d313e84033b4#key-1",
            "privKey": "tBjxEJCqSkj7u+iTWRqAVZwtcl2XBZrlaMfhxYIRc4wj7epbmDkJ35sCin3MWnAxvHJNDY0yyPafVspsrgb1Ng=="
        }
    ]
}
  • resource-data-file: path to the resource file (e.g.: /path/to/resource.jpeg)

Queries

GetCollectionResources

cheqd Cosmos CLI Example:

cheqd-noded query resource collection-metadata [collection-id]

GetResource

cheqd Cosmos CLI Example:

cheqd-noded query resource specific-resource [collectionId] [id]

QueryResourceMetadata

cheqd Cosmos CLI Example:

cheqd-noded query resource resource-metadata [collectionId] [id]

Resource versioning

Resource are immutable, but it is possible to create new versions of it under a new identifier(id field). When creating a resource whose fields collection_id, name and resource_type match an existing resource:

  • The latest version of the current resource will be added with a link to the new one. That is, field next_version_id will contain the new resource identifier.

  • A new resource with data from the transaction will be created with the previous version resource id in field previousVersionId.

Example:

Step 1. Resource exists in the ledger:

{
  "resource": {
    "collection_id": "91e5f0cf-5f1e-5c19-97d3-d313e84033b4",  // Common collection ID
    "id": "54cb8b4d-af33-4606-bc54-0f035ee30e0f", // Old Resource ID and version number
    "name": "PassportScheme", // Resource name must remain the same
    "version": "1.0", // Resource version
    "resource_type": "CL-Schema", // Resource type must remain the same
    "also_known_as": [
        {
            "uri": "https://example.com/alternative-uri",
            "description": ""
        },
        {
            "uri": "https://example.com/alternative-uri",
            "description": "Alternative URI description"
        }
    ],
    "media_type": "application/json",
    "created": "2022-07-19T08:40:00Z",
    "checksum": "7b2022636f6e74656e74223a202274657374206461746122207d0ae3b0c44298", // Old version checksum
    "previous_version_id": "", // an empty string, since no previous version
    "next_version_id": "" // an empty string, since no next version
  }
}

Step 2. Client send request for creating a new resource with a transaction MsgCreateResource

MsgCreateResource for creating Resource2
{
  "collectionId":   "91e5f0cf-5f1e-5c19-97d3-d313e84033b4", // same collection ID
  "id":             "bb2118f3-5e55-4510-b420-33ef9e1726d2", // new unique ID
  "name":           "PassportSchema", // same resource name
  "resourceType":   "CL-Schema", // same resource type
  
  "data":           ...
}

Step 3. After the transaction is applied

"resources": [
  { // First version of a Resource called PassportSchema
    "collection_id": "91e5f0cf-5f1e-5c19-97d3-d313e84033b4",  // Common collection ID
    "id": "54cb8b4d-af33-4606-bc54-0f035ee30e0f", // Old Resource ID and version number
    "name": "PassportScheme", // Resource name must remain the same
    "version": "1.0", // Resource version
    "resource_type": "CL-Schema", // Resource type must remain the same
    "also_known_as": [
        {
            "uri": "https://example.com/alternative-uri",
            "description": ""
        },
        {
            "uri": "https://example.com/alternative-uri",
            "description": "Alternative URI description"
        }
    ],
    "media_type": "application/json",
    "created": "2022-07-19T08:40:00Z",
    "checksum": "7b2022636f6e74656e74223a202274657374206461746122207d0ae3b0c44298", // Old version checksum
    "previous_version_id": "", // an empty string, since no previous version
    "next_version_id": "bb2118f3-5e55-4510-b420-33ef9e1726d2", // Points to next version below
  },
  { // Second version of a Resource called PassportSchema
    "collection_id": "91e5f0cf-5f1e-5c19-97d3-d313e84033b4", // Common collection ID
    "id": "bb2118f3-5e55-4510-b420-33ef9e1726d2", // New Resource ID and version number
    "name": "PassportScheme", // Resource name must remain the same
    "version": "1.0", // Resource version
    "resource_type": "CL-Schema", // Resource type must remain the same
    "also_known_as": [
        {
            "uri": "https://example.com/alternative-uri",
            "description": ""
        },
        {
            "uri": "https://example.com/alternative-uri",
            "description": "Alternative URI description"
        }
    ],
    "media_type": "application/json",
    "created": "2022-07-19T08:40:00Z",
    "checksum": "7b2022636f6e74656e74223a202274657374206461746122207d0ae3b0c44298", // Old version checksum
    "previous_version_id": "54cb8b4d-af33-4606-bc54-0f035ee30e0f", // Points to previous version above
    "next_version_id": "" // an empty string, since no next version
  }
]

Decision

Assumptions

  • Immutability:

    • Resources on-ledger are immutable, so can't be destroyed;

  • Limitations

    • Resource size is now limited by maximum tx/block size;

Future improvements

  • Limitations

    • Introduce module level resource size limit that can be changed by voting

Resources module on ledger

A new module will be created: resource.

Dependencies

  • It will have cheqd module as a dependency.

    • Will be used for DIDs existence checks.

    • Will be used for authentication

References

Last updated