-
Notifications
You must be signed in to change notification settings - Fork 79
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Formalize the use of the endpoint ATTRIB and rendering the DID Document #296
Changes from 2 commits
863ce76
1420c50
3f107d7
15b1b6d
ff3b0b0
84a1be8
97e6491
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -125,60 +125,22 @@ <h3>Create (Register)</h3> | |
in that case it is assumed that the key has all authorisations. | ||
</p> | ||
<p>For example:</p> | ||
<p>To create a DID, you must submit a transaction that looks like this: <br> | ||
<p>To create a DID, you must submit a <a href="https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#nym">"NYM"</a> transaction that looks like this: <br> | ||
<pre> | ||
{ | ||
"submitterId": <Trust Anchor DID>, | ||
"signature": <signature over this transaction from the Trust Anchor>, | ||
"reqId": <A nonce for this transaction>, | ||
"operation": { | ||
"type": "NYM", | ||
"did": <new DID that is being registered>, | ||
"document": { | ||
"publicKey": [{ | ||
"id": <a valid unique identifier>, | ||
"type": <ed25519, defined in appendix>, | ||
"publicKeyBase58": <Key material encoded>, | ||
"authorizations": ["ALL"] | ||
}], | ||
"authentication": [{ | ||
"type": <type of DID authentication>, | ||
"publicKey": "<reference to a publicKey object>", | ||
}], | ||
"service": [{ | ||
"type": <agentService, emailService, apiService, dnsService, etc>, | ||
"serviceEndpoint": <A URI for the endpoint> | ||
}], | ||
"role":<Optional; Enumeration for roles, an integer; if left empty then no special role is assigned> | ||
} | ||
} | ||
'operation': { | ||
'type': <Transaction type -- NYM >, | ||
'dest': <new DID that is being registered>, | ||
'role': <Role given to the new DID -- based on network config>, | ||
'verkey': <Key material encoded>, | ||
}, | ||
|
||
'identifier': 'L5AD5g65TDQr1PPHHRoiGf' <Trust Anchor DID>, | ||
'reqId': <A nonce for this transaction>, | ||
'protocolVersion': 2, | ||
'signature': <signature over this transaction from the Trust Anchor> | ||
} | ||
|
||
Example | ||
{ | ||
"submitterId": "did:sov:29wksjcn38djfh47ruqrtcd5", | ||
"signature": "1qaz2wsx3edc4rfv5tgb6yhn7ujm8iklop==", | ||
"reqId": "okn987yhbgFtErDsCXsw", | ||
"operation": { | ||
"type": "NYM", | ||
"did": "did:sov:mnjkl98uipsndg2hdjdjuf7", | ||
"document": { | ||
"publicKey": [{ | ||
"id": "key1" | ||
"type": "ED25519SignatureVerification", | ||
"publicKeyBase58": "..." | ||
}], | ||
"authentication": [{ | ||
"type": "ED25519SigningAuthentication", | ||
"publicKey": "key1" | ||
}], | ||
"service": [{ | ||
"type": "agentService", | ||
"serviceEndpoint":"https://www.sovrin.org/agents" | ||
}] | ||
} | ||
} | ||
} | ||
</pre> The transaction must be signed by a Trust Anchor and must provide an un-registered DID and a document of data | ||
about that DID. Possible outcomes from the create operation include: | ||
<ul> | ||
|
@@ -187,27 +149,11 @@ <h3>Create (Register)</h3> | |
<li>SUCCESS: "Contains the sequence number of the transaction and the merkle proof"</li> | ||
</ul> | ||
</p> | ||
<p>The DID document is rendered as follows. The exact data structure persisted on the ledger does not have to match the following and can vary for each node. | ||
<pre> | ||
{ | ||
"id": "did:sov:mnjkl98uipsndg2hdjdjuf7", | ||
"publicKey": [{ | ||
"id": "key1" | ||
"type": "ED25519SignatureVerification", | ||
"publicKeyBase58": "...", | ||
"authorizations": ["all"] | ||
}], | ||
"authentication": [{ | ||
"type": "ED25519SigningAuthentication", | ||
"publicKey": "key1" | ||
}], | ||
"service": [{ | ||
"type": "agentService", | ||
"serviceEndpoint":"https://www.sovrin.org/agents" | ||
}] | ||
} | ||
</pre> | ||
</p> | ||
|
||
<h4>DID Service Endpoint</h4> | ||
<p>By convention, Indy DID Controllers often create an Indy ATTRIB object (using the <a href="https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#attrib">"ATTRIB"</a> | ||
transaction) related to the NYM with a raw value of "endpoint", and a URL that represents the service endpoint for the DID. As noted in the "Read" section of this specification | ||
such an "endpoint" ATTRIB is read when resolving a DID and it's data included in the resulting DID Document. | ||
|
||
<h3>Read (Resolve)</h3> | ||
<p>A Sovrin DID record can be looked up directly by the DID using a standard Sovrin GET_NYM transaction that simply | ||
|
@@ -227,10 +173,111 @@ <h3>Read (Resolve)</h3> | |
} | ||
} | ||
</pre> | ||
<p>Anyone can query a DID record, by sending the above request. The response contains the DID document.</p> | ||
<p>Anyone can query a DID record, by sending the above request. The response contains the data that can be used to create a DID document, as noted below.</p> | ||
|
||
<h4>DID Service Endpoint</h4> | ||
|
||
<p>When reading (resolving) a Sovrin DID, in addition to querying the DID record, a query MUST be performed for a related DID Service endpoint. | ||
This is done by executing the <a href="https://hyperledger-indy.readthedocs.io/projects/node/en/latest/requests.html#get_attrib">"GET_ATTRIB"</a> | ||
transaction. The format of the GET_ATTRIB request looks like | ||
</p> | ||
<pre> | ||
{ | ||
"submitterId": <Optional; DID of the author of this query>, | ||
"reqId": <Optional; a nonce for this query>, | ||
"identifier": <Required; The DID being read/resolved>, | ||
"operation": { | ||
"raw": "endpoint" <Required; the value must be endpoint> | ||
} | ||
} | ||
</pre> | ||
|
||
<p>The relevant part of the response from the successful execution of such a GET_ATTRIB request is:</p> | ||
|
||
<pre> | ||
{ | ||
... | ||
'data': '{"endpoint":{"endpoint":"https://example.com/endpoint"}}', | ||
'dest': 'AH4RRiPR78DUrCWatnCW2w' < The DID being read/resolved>, | ||
'raw': 'endpoint', | ||
... | ||
} | ||
</pre> | ||
|
||
<p>The inner value of "endpoint" ("https://example.com/endpoint" in the example above) is the service endpoint for the DID. | ||
Anyone can query an ATTRIB by sending the above request. The service endpoint is used to create the service block of a DID document, as noted below.</p> | ||
|
||
<h4>Resolver DID Document Format</h4> | ||
|
||
<p>The DID Document is generated by a resolver (not by the Indy/Sovrin ledger) by taking the GET_NYM and optional GET_ATTRIB data and | ||
rendering a DID Document as follows. See the notes following the rendering, and the next section that lists an earlier version | ||
of the rendered document. | ||
</p> | ||
|
||
<pre> | ||
{ | ||
"@context": [ | ||
"https://www.w3.org/ns/did/v1" | ||
], | ||
"id": "did:sov:HR6vs6GEZ8rHaVgjg2WodM", | ||
"verificationMethod": [ | ||
{ | ||
"type": "Ed25519VerificationKey2018", | ||
"id": "did:sov:HR6vs6GEZ8rHaVgjg2WodM#key-1", | ||
"publicKeyBase58": "9wvq2i4xUa5umXoThe83CDgx1e5bsjZKJL4DEWvTP9qe" | ||
} | ||
], | ||
"service": [ | ||
{ | ||
"type": "endpoint", | ||
"serviceEndpoint": "https://example.com/endpoint" | ||
}, | ||
{ | ||
"id": "did:sov:HR6vs6GEZ8rHaVgjg2WodM#did-communication", | ||
"type": "did-communication", | ||
"priority" : 0, | ||
"recipientKeys" : [ "did:sov:HR6vs6GEZ8rHaVgjg2WodM#key-1" ], | ||
"routingKeys" : [ ], | ||
"accept": [ | ||
"didcomm/aip2;env=rfc19" | ||
], | ||
"serviceEndpoint": "https://example.com/endpoint" | ||
}, | ||
{ | ||
"id": "did:sov:HR6vs6GEZ8rHaVgjg2WodM#didcomm-1", | ||
"type": "DIDComm", | ||
"serviceEndpoint": "https://example.com/endpoint", | ||
"routingKeys": [ ] | ||
} | ||
], | ||
"authentication": [ | ||
{ | ||
"type": "Ed25519VerificationKey2018", | ||
"id": "did:sov:HR6vs6GEZ8rHaVgjg2WodM#key-1", | ||
"publicKeyBase58": "9wvq2i4xUa5umXoThe83CDgx1e5bsjZKJL4DEWvTP9qe" | ||
} | ||
], | ||
"assertionMethod": [ | ||
{ | ||
"type": "Ed25519VerificationKey2018", | ||
"id": "did:sov:HR6vs6GEZ8rHaVgjg2WodM#key-1", | ||
"publicKeyBase58": "9wvq2i4xUa5umXoThe83CDgx1e5bsjZKJL4DEWvTP9qe" | ||
} | ||
] | ||
} | ||
</pre> | ||
|
||
<h5>Notes</h5> | ||
|
||
<p>The following are notes about the example DID Document above: | ||
<ul> | ||
<li>The DID Document text is boilerplate except for the "did:sov..." identifier, the public key ("publicKeyBase58) and the references to the service endpoint ("serviceEndpoint").</li> | ||
<li>If there is no result from the GET_ATTRIB transaction, the "service" block is not included in the document.</li> | ||
<li>The three entries in the service block cover the three "eras" of DIDComm, AIP 1.0 (type "endpoint"), AIP 2.0 (type "did-communication") and DIDComm V2 (type "DIDComm").</li> | ||
<li>Because there is just one endpoint specified, the service blocks cannot make use of the DIDComm routing capabilities for messaging based on the DIDDoc.</li> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't understand this comment. I see several different places where There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Daniel -- I'm trying If you look at the Universal Resolver, it's output includes that information, in the form of the simple "endpoint" service block. What this PR does is just expand the single "endpoint" we have based on the convention being used, and provide proper DIDComm service blocks for AIP 2.0 and DIDComm V2. Assuming we don't change the convention, we have no where to specify a mediator/routingKeys -- hence that is hard-wired to be an empty array. What we really want is Make sense? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, yes. Thank you for the clarification. That helps a lot. With this PR, would it be possible to start up a new convention (without writing any new code in the ledger--only in clients) where the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd really like to the see the focus put on did:indy for that, so I personally would prefer not. I suppose it would take off the pressure to get did:indy implemented. I'd love to know how much people would use this if we did it now. Easy enough though -- like this I think: {
"endpoint": {
"endpoint": "http://example.com/didService",
"routingKeys": [ "did:key:12345", "did:key:67890" ]
}
}
BTW -- my reply to you above was terrible. Just noticed my opening line was the opposite of intended. I said "I'm trying not to limit", but meant to say "I'm trying There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see the logic of limiting coding effort -- but what I'm proposing is not extra coding. It's actually less coding. Instead of hard-coding some logic into UR that always adds an empty My suggestion is motivated by a lack of confidence in the timeline for did:indy. If we had did:indy today, I wouldn't suggest it at all, because we'd have a better alternative. Perhaps I'm being pessimistic to want to leave us a pressure release valve. Or perhaps there are reasons not to do this that I haven't considered. I wasn't advocating super strongly -- just thinking out loud. I'm content to have the PR merged as-is, if others feel that's the best tradeoff. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not as simple as you say. First, the resolver has to know about each As noted, we could add an extra format for "endpoint" to add I agree that "did:indy" won't be around for awhile. However, I think this will be sufficient for the institutional issuers we have today -- the main use case we have for DIDs on Indy. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, got it. I had forgotten that each |
||
</ul> | ||
</p> | ||
|
||
<h3>Update (Replace)</h3> | ||
<p>To replace the DID document, the owner or guardian (guardianship ends once ownership begins) of the DID should send the following | ||
transaction using a key referenced in the <strong>authentication</strong> property. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"it's data" is a misspelling; should be without the apostrophe: "its data"