SHR DID Method Specification
Table of Contents
- Preface
- Abstract
- Technical Specifications
- CRUD Operation Definitions
- Metadata
- Resolving DID URIs with Query Parameters
- Reference Implementations
- References
Preface
The SHR DID method specification adheres to the requirements outlined in the W3C DID specification, which is currently published by the W3C Credentials Community Group. It is largely derived from the ETHR DID method specification, with specific adjustments made to accommodate the unique requirements of the ShareRing ecosystem. This document outlines the technical standards for decentralized identifiers on ShareLedger.
Abstract
Identifier Controller
By default, each identifier is controlled by itself—or more accurately, by its corresponding private key. This means that a single public key controls the identifier at any given time. However, the controller has the flexibility to replace themselves with any other public key. This capability extends to contracts, enabling more advanced control models such as multi-signature governance.
Target System
The target system for this method is the ShareLedger network, specifically where the CW1056 smart contract registry is deployed. This registry serves as the on-chain anchor for all identity operations.
Advantages
This method offers several key benefits designed to optimize efficiency and privacy:
Zero-Cost Creation: No transaction fees are required to create an identifier.
Privacy: Identifier creation is performed locally and remains private until an on-chain interaction occurs.
Account Abstraction: It fully leverages ShareLedger’s built-in account abstraction capabilities.
Flexible Control: It supports multi-sig or proxy wallets acting as account controllers.
Standardized Keys: It natively supports secp256k1 public keys as identifiers.
Data Decoupling: It decouples claims data and ShareLedger interactions from the underlying identifier, enhancing modularity.
Gas Flexibility: It allows third-party funding services to pay gas fees via meta-transactions (“receiver pays”).
Interoperability: It is compatible with any CosmWasm-compliant blockchain.
Versioning: It supports native, verifiable versioning of DID documents.
Technical Specifications
JSON-LD Context Definition
Because this DID method supports both publicKeyHex and publicKeyBase64 encodings for verification methods, it requires a valid JSON-LD context for those entries. To ensure proper JSON-LD processing, the @context used when constructing DID documents for did:shr should be defined as follows:
"@context": [
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/suites/secp256k1recovery-2020/v2"
]You must include this @context if you intend to use EcdsaSecp256k1RecoveryMethod2020 in your applications.
DID Method Name
The method name is shr.
Method Specific Identifier
The method-specific identifier is represented as the HEX-encoded secp256k1 public key (in compressed form), or the corresponding HEX-encoded ShareLedger address on the target network, prefixed with shareledger.
Syntax:
Plaintext
shr-did = "did:shr:" shr-specific-identifier
shr-specific-identifier = [ shr-token-type ":" ] public-key-hex
shr-token-type = "shr" /"dvct" / "sbt"
public-key-hex = "0x" 66*HEXDIGNotes:
“shr”refers to the VCT token type.The public key must strictly follow these rules:
It MUST be represented in compressed form (refer to the Secp256k1 Wiki).
The corresponding
blockchainAccountIdentry is automatically added to the default DID document, unless the owner property has been changed to a different address.All Read, Update, and Delete operations MUST be made using the corresponding
blockchainAccountIdand MUST originate from the correct controller account (the CW1056 owner).In the case of an issuer, the issuer will be the owner of the DID.
Relationship to VCT – CRUD Operation Definitions
Create (Register)
To create a shr DID, a public key (i.e., a key pair) needs to be generated locally. At this stage, no interaction with the target ShareLedger blockchain is required. Registration is implicit: it is computationally impossible to brute-force a public key (i.e., guess the private key for a given public key on the secp256k1 curve). The holder of the private key is inherently the entity identified by the DID.
The default DID document for a did:shr:<public key>—for example, did:shr:02d04758db085b20793240eddc393c8850ec2587cb57d8f9baf1acbb6f34cbabc7—with no prior transactions to the CW1056 registry looks like this:
{
"@context": [
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/suites/secp256k1recovery-2020/v2"
],
"id": "did:shr:02457f5dbfdde10a2b8ad3ed73a6abcdbf4e7c326766e5b9f235b0af0b4846f0d9",
"verificationMethod": [
{
"id": "did:shr:02457f5dbfdde10a2b8ad3ed73a6abcdbf4e7c326766e5b9f235b0af0b4846f0d9#controller",
"type": "EcdsaSecp256k1RecoveryMethod2020",
"controller": "did:shr:02457f5dbfdde10a2b8ad3ed73a6abcdbf4e7c326766e5b9f235b0af0b4846f0d9",
"blockchainAccountId": "cosmos:shareledger:1zq5e4w2w552t4slmclezsuxu5jupmpu52ut6vd"
},
{
"id": "did:shr:02457f5dbfdde10a2b8ad3ed73a6abcdbf4e7c326766e5b9f235b0af0b4846f0d9#controllerKey",
"type": "EcdsaSecp256k1VerificationKey2019",
"controller": "did:shr:02457f5dbfdde10a2b8ad3ed73a6abcdbf4e7c326766e5b9f235b0af0b4846f0d9",
"publicKeyHex": "02457f5dbfdde10a2b8ad3ed73a6abcdbf4e7c326766e5b9f235b0af0b4846f0d9"
}
],
"authentication": [
"did:shr:02457f5dbfdde10a2b8ad3ed73a6abcdbf4e7c326766e5b9f235b0af0b4846f0d9#controller"
],
"assertionMethod": [
"did:shr:02457f5dbfdde10a2b8ad3ed73a6abcdbf4e7c326766e5b9f235b0af0b4846f0d9#controller"
]
}Read (Resolve)
The DID document is dynamically built by querying read-only functions and interpreting contract events from the CW1056 registry. Additional verification relationships and service entries are added or removed by enumerating these contract events.
Controller Address
Each identifier always has a controller address. The resolver MUST first check the read-only contract function identityOwner(address identity) on the deployed CW1056 contract to determine the current owner.
This controller address MUST be represented in the DID document as a verificationMethod entry. The id is set as the DID being resolved with the fragment #controller appended. A reference to this entry MUST also be added to both the authentication and assertionMethod arrays of the DID document.
Enumerating Contract Events to build the DID Document
The CW1056 contract publishes three specific types of events for each identifier:
change_did_owneraction (indicating a change of controller)change_did_delegateactionchange_did_attributeaction
If a change has ever been made for the ShareLedger address of an identifier, the block number of the most recent change is stored in the contract’s changed mapping. The latest event can be efficiently located by checking for one of the three event types at that exact block height. Each CW1056 event also contains a previous_change value, which points to the block number of the preceding change (if any), creating a linked list of history.
To retrieve the full history of changes for an address, use the following logic:
Query
changed(String identity)on the CW1056 contract to get the latest block where a change occurred.If the result is null, return (no changes).
Filter for the above event types emitted by the contract address at that specified block.
If the event has a
previous_changevalue, repeat step 3 for that block number.
After reconstructing the history of events for an address, interpret each event to build the DID document as follows:
Controller changes (change_did_owner)
When the controller address of a did:shr is updated, a change_did_owner action event is emitted. The data from this event MUST be used to update the #controller entry in the verificationMethod array. When resolving DIDs with publicKey identifiers, if the controller (owner) address differs from the address corresponding to the publicKey, then the #controllerKey entry in the verificationMethod array MUST be omitted.
[
{
"events": [
{
"type": "wasm",
"attributes": [
{
"key": "_contract_address",
"value": "shareledger1lfhglvsc425mhdlzv582pp5qvaxmgv94v0jgawy9quz2exkvk8qsd4c5lm"
},
{
"key": "action",
"value": "change_did_owner"
},
{
"key": "owner",
"value": "shareledger10kwzf5eq0fz83kxtvw2sqed3qktz2834t4kyxezh2vgfsj2cnkqqzkaxa3"
},
{
"key": "previous_change",
"value": "1000"
}
]
}
],
"msg_index": 0
}
]Delegate Keys (change_did_delegate)
Delegate keys are ShareLedger or ICA addresses that can serve as general signing keys or optionally perform authentication. These delegates are verifiable directly from the deployed CW1056 smart contract.
When a delegate is added or revoked, a change_did_delegate event is published. This event MUST be used to update the DID document.
identity: The ShareLedger address that is being delegated to.
delegate_types: There are two types currently published in the DID document:
veriKey: This adds a
EcdsaSecp256k1RecoveryMethod2020to theverificationMethodsection of the DID document with theblockchainAccountId(shareledgerAddress)of the delegate. It also adds a reference to it in theassertionMethodsection.sigAuth: This adds a
EcdsaSecp256k1RecoveryMethod2020to theverificationMethodsection of the document and a reference to it in theauthenticationsection.
Only events with a validTo timestamp (measured in seconds) greater than or equal to the current time should be included in the DID document. When resolving an older version (using versionId in the didURL query string), the validTo entry MUST be compared against the timestamp of the block at versionId height.
Such valid delegates MUST be added to the verificationMethod array as EcdsaSecp256k1RecoveryMethod2020 entries, with the delegate address listed in the blockchainAccountId property and prefixed with cosmos:shareledger:, in accordance with CAIP10.
Example:
[
{
"events": [
{
"type": "wasm",
"attributes": [
{
"key": "_contract_address",
"value": "shareledger1lfhglvsc425mhdlzv582pp5qvaxmgv94v0jgawy9quz2exkvk8qsd4c5lm"
},
{
"key": "action",
"value": "change_did_delegate"
},
{
"key": "identity",
"value": "shareledger1lfhglvsc425mhdlzv582pp5qvaxmgv94v0jgawy9quz2exkvk8qsd4c5lm"
},
{
"key": "delegate_type",
"value": "veriKey"
},
{
"key": "valid_to",
"value": "2000"
},
{
"key": "previous_change",
"value": "1000"
}
]
}
],
"msg_index": 0
}
]Non-Shareledger Attributes (DIDAttributeChanged)
Attributes allow for the addition of non-ShareLedger keys, service endpoints, and other metadata. Attributes exist solely on the blockchain as contract events and cannot be queried directly from within CosmWasm code.
name:
[
{
"events": [
{
"type": "wasm",
"attributes": [
{
"key": "_contract_address",
"value": "shareledger1lfhglvsc425mhdlzv582pp5qvaxmgv94v0jgawy9quz2exkvk8qsd4c5lm"
},
{
"key": "action",
"value": "change_did_delegate"
},
{
"key": "identity",
"value": "shareledger1lfhglvsc425mhdlzv582pp5qvaxmgv94v0jgawy9quz2exkvk8qsd4c5lm"
},
{
"key": "delegate_type",
"value": "veriKey"
},
{
"key": "previous_change",
"value": "1000"
}u
]
}
],
"msg_index": 0
}
]While any attribute can be stored, for the purpose of the DID document, we strictly support adding to the following sections:
Public Keys (Verification Methods)
Service Endpoints
This design decision is intentional, aiming to discourage the use of custom attributes in DID documents to prevent the misuse of the chain for storing personal user information.
Public Keys
The name of the attribute added to CW1056 should follow this format: did/pub/(Secp256k1|RSA|Ed25519|X25519)/(veriKey|sigAuth|enc)/(hex|base64|base58)
(Essentially: did/pub/<key algorithm>/<key purpose>/<encoding>)
Please opt for the base58 encoding, as other encodings are not spec-compliant and will be removed in future versions of the spec and reference resolver.
Key purposes
veriKey: Adds a verification key to the
verificationMethodsection of the document and adds a reference to it in theassertionMethodsection.sigAuth: Adds a verification key to the
verificationMethodsection of the document and adds a reference to it in theauthenticationsection.enc: Adds a key agreement key to the
verificationMethodsection and a corresponding entry to thekeyAgreementsection. This key is used to perform a Diffie-Hellman key exchange and derive a secret key for encrypting messages to the DID that lists such a key.
Note: The
<encoding>refers only to the key encoding in the resolved DID document. Attribute values sent to the CW1056 registry should always be hex encodings of the raw public key data.
Example: Hex encoded Secp256k1 Verification Key
A DIDAttributeChanged event for the account shareledger1zq5e4w2w552t4slmclezsuxu5jupmpu52ut6vd with the name did/pub/Secp256k1/veriKey/hex and the value 02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71 generates a verification method entry like the following:
{
"id": "did:shr:02457f5dbfdde10a2b8ad3ed73a6abcdbf4e7c326766e5b9f235b0af0b4846f0d9#delegate-1",
"type": "EcdsaSecp256k1VerificationKey2019",
"controller": "did:shr:02457f5dbfdde10a2b8ad3ed73a6abcdbf4e7c326766e5b9f235b0af0b4846f0d9",
"publicKeyHex": "02b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71"
}Example: Base58 encoded Ed25519 Verification Key
A DIDAttributeChanged event for the account shareledger1zq5e4w2w552t4slmclezsuxu5jupmpu52ut6vd with the name did/pub/Ed25519/veriKey/base58 and the value b97c30de767f084ce3080168ee293053ba33b235d7116a3263d29f1450936b71 generates a verification method entry like this:
{
"id": "did:shr:02457f5dbfdde10a2b8ad3ed73a6abcdbf4e7c326766e5b9f235b0af0b4846f0d9#delegate-1",
"type": "Ed25519VerificationKey2018",
"controller": "did:shr:02457f5dbfdde10a2b8ad3ed73a6abcdbf4e7c326766e5b9f235b0af0b4846f0d9",
"publicKeyBase58": "DV4G2kpBKjE6zxKor7Cj21iL9x9qyXb6emqjszBXcuhz"
}Example: Base64 encoded X25519 Encryption Key
A DIDAttributeChanged event for the account shareledger1zq5e4w2w552t4slmclezsuxu5jupmpu52ut6vd with the name did/pub/X25519/enc/base64 and the value 302a300506032b656e032100118557777ffb078774371a52b00fed75561dcf975e61c47553e664a617661052 generates a verification method entry like this:
{
"id": "did:shr:02457f5dbfdde10a2b8ad3ed73a6abcdbf4e7c326766e5b9f235b0af0b4846f0d9#delegate-1",
"type": "X25519KeyAgreementKey2019",
"controller": "did:shr:02457f5dbfdde10a2b8ad3ed73a6abcdbf4e7c326766e5b9f235b0af0b4846f0d9",
"publicKeyBase64": "MCowBQYDK2VuAyEAEYVXd3/7B4d0NxpSsA/tdVYdz5deYcR1U+ZkphdmEFI="
}Service Endpoints
The name of the attribute should follow this format: did/svc/[ServiceName]
Example:
A DIDAttributeChanged event for the account shareledger1lfhglvsc425mhdlzv582pp5qvaxmgv94v0jgawy9quz2exkvk8qsd4c5lm with the name did/svc/HubService and value of the URL https://hubs.uport.me hex encoded as 0x68747470733a2f2f687562732e75706f72742e6d65 generates a service endpoint entry like the following:
{
"id": "did:shr:02457f5dbfdde10a2b8ad3ed73a6abcdbf4e7c326766e5b9f235b0af0b4846f0d9#service-1",
"type": "HubService",
"serviceEndpoint": "https://auth.sharering.network"
}id properties of entries
With the exception of #controller and #controllerKey, the id properties that appear throughout the DID document MUST be stable across updates. This ensures that the same key material remains referenced by the same ID even after an update occurs.
Attribute or delegate changes that result in
verificationMethodentries MUST set the id to${did}#delegate-${eventIndex}.Attributes that result in service entries MUST set the id to
${did}#service-${eventIndex}.
Here, eventIndex is the index of the event that modifies that specific section of the DID document.
Example
add key =>
#delegate-1is addedadd another key =>
#delegate-2is addedadd delegate =>
#delegate-3is addedadd service =>
#service-1is addedrevoke first key =>
#delegate-1gets removed from the DID document;#delegate-2and#delegate-3remain.add another delegate =>
#delegate-5is added (note: earlier revocation is counted as an event)first delegate expires =>
delegate-3is removed,#delegate-5remains intact
Update
The DID Document may be updated by invoking the relevant smart contract functions as defined by the CW1056 standard. These updates can include changes to the account owner, adding delegates, and adding additional attributes. For a comprehensive description, please refer to the detailed CW1056 documentation.
Invoking these functions triggers the respective contract events, which are then used to reconstruct the DID Document for a given account as described in the Enumerating Contract Events to build the DID Document section.
Additionally, some elements of the DID Document will be revoked automatically when their validity period expires. This applies to both delegates and additional attributes. All attribute and delegate functions trigger the respective events, which are essential for building the DID Document for a given identifier.
Delete (Revoke)
To revoke a DID, the owner property of the identifier MUST be set to None. This indicates that the account has no owner—a common approach for invalidation (similar to burning tokens). Once this change is made, it is impossible to make further updates to the DID document; therefore, all pre-existing keys and services MUST be considered revoked.
If the intention is to revoke all signatures corresponding to the DID, this option MUST be used.
The DID resolution result for a deactivated DID has the following shape:
{
"didDocumentMetadata": {
"deactivated": true
},
"didResolutionMetadata": {
"contentType": "application/did+ld+json"
},
"didDocument": {
"@context": "https://www.w3.org/ns/did/v1",
"id": "<the deactivated DID>",
"verificationMethod": [],
"assertionMethod": [],
"authentication": []
}
}Metadata
The resolution method returns an object containing the following properties: didDocument, didDocumentMetadata, and didResolutionMetadata.
DID Document Metadata
When resolving a DID document that has undergone updates, the details of the latest update MUST be listed in the didDocumentMetadata.
versionIdMUST be the block number of the latest update.updatedMUST be the ISO date string of the block time of the latest update (without sub-second resolution).
Example:
{
"didDocumentMetadata": {
"versionId": "12090175",
"updated": "2021-03-22T18:14:29Z"
}
}DID Resolution Metadata
{
"didResolutionMetadata": {
"contentType": "application/did+ld+json"
}
}Resolving DID URIs with Query Parameters
versionId query string parameter
This DID method supports resolving previous versions of the DID document by specifying a versionId parameter.
Example: did:shr:02457f5dbfdde10a2b8ad3ed73a6abcdbf4e7c326766e5b9f235b0af0b4846f0d9?versionId=12090175
The versionId corresponds to the block number at which the DID resolution MUST be performed. Only CW1056 events prior to or contained within this block number are considered when reconstructing the event history.
If there are any events after that block that mutate the DID, the earliest of them SHOULD be used to populate the properties of the didDocumentMetadata:
nextVersionIdMUST be the block number of the next update to the DID document.nextUpdateMUST be the ISO date string of the block time of the next update (without sub-second resolution).
In cases where the DID has had updates prior to or included in the versionId block number, the updated and versionId properties of the didDocumentMetadata MUST correspond to the latest block prior to the versionId query string parameter.
Any timestamp comparisons of validTo fields in the event history MUST be performed against the versionId block timestamp.
Example: ?versionId=12101682
{
"didDocumentMetadata": {
"versionId": "12090175",
"updated": "2021-03-22T18:14:29Z",
"nextVersionId": "12276565",
"nextUpdate": "2021-04-20T10:48:42Z"
}
}Security considerations of DID versioning
Applications MUST take precautions when using versioned DID URIs. Even if a key is compromised and subsequently revoked, it could still be used to issue signatures on behalf of an “older” version of the DID URI. The use of versioned DID URIs is recommended only in limited situations where the timestamp of signatures can be reliably verified, where malicious signatures can be easily revoked, or where applications can afford to check for explicit revocations of keys or signatures. Whenever versioned DIDs are in use, it SHOULD be made obvious to users that they are interacting with potentially revoked data.
initial-state query string parameter
TBD
Reference Implementations
TBD
References
[1] W3C DID Core Specification: https://w3c-ccg.github.io/did-core/
[2] Ethereum EIP-1056: ethereum/EIPs#1056
[3] ETHR DID Resolver: https://github.com/decentralized-identity/ethr-did-resolver
[4] ETHR DID Registry: https://github.com/uport-project/ethr-did-registry
[5] ETHR DID Method Spec: Reference Documentation