Closed tplooker closed 4 years ago
This is a dupe of https://github.com/decentralized-identity/sidetree/issues/777
though your code examples are better.
See also https://github.com/w3c/did-core/issues/337 @tplooker can you maybe provide these examples on did core, and lets get the WG position on this issue?
This is intentional, because if you are using an unachored DID and only include the short-form suffix of the ID in your resulting DID Document, there is a good chance a counter party could end up including the ID in a credential or other form of data that would subsequently be unresolvable, given it doesn't contain the long-form data. The Sidetree reference and ION return a DID Doc with the short-form ID when it detects an ID is anchored.
@csuwildcat I understand that there should be an explicit indication that the did document is not anchored but I dont think that this should manifest as the long form id in the did document, how about instead return the initial state in the method specific metadata and a flag indicating whether or not the did is anchored?
E.g the the request
curl http://localhost:3000/identifiers/did:ion:test:EiC0wVS3K9VLv-XZVL55i8Y4d1Hk1kJpeckfDXIu0nM17A?-ion-initial-state=eyJkZWx0YV9oYXNoIjoiRWlDUlRKZ2Q0U0V2YUZDLW9fNUZjQnZJUkRtWF94Z3RLX3gwV0gtZXVhNGhrZyIsInJlY292ZXJ5X2NvbW1pdG1lbnQiOiJFaURmR2k0NzRpajQ5VnFfeWlLRzFxOG9DMWpIX0JGTGZIMjdCSVFLVW5UQlR3In0.eyJ1cGRhdGVfY29tbWl0bWVudCI6eyIwIjoxOCwiMSI6MzIsIjIiOjIyMywiMyI6MjYsIjQiOjQ2LCI1Ijo1OSwiNiI6MjI2LCI3Ijo0MCwiOCI6MjQ4LCI5IjoyNDUsIjEwIjo5MCwiMTEiOjE5MSwiMTIiOjIwMiwiMTMiOjM0LCIxNCI6MTM0LCIxNSI6MjE0LCIxNiI6MTc1LCIxNyI6NDAsIjE4IjoxMSwiMTkiOjg4LCIyMCI6MTk5LCIyMSI6MjUyLCIyMiI6MTcsIjIzIjo3NSwiMjQiOjEyNCwiMjUiOjEyNSwiMjYiOjE4NywiMjciOjQsIjI4IjoxMzIsIjI5IjoxMCwiMzAiOjgyLCIzMSI6MTE2LCIzMiI6MTkzLCIzMyI6Nzl9LCJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljX2tleXMiOlt7ImlkIjoielEzc2hta2VNZmh1RHdOOGExS3YiLCJ0eXBlIjoiU2VjcDI1NmsxVmVyaWZpY2F0aW9uS2V5MjAxOCIsImp3ayI6eyJrdHkiOiJFQyIsImNydiI6InNlY3AyNTZrMSIsIngiOiJhYTZhNDVhM1lnRW1sYks2cjVwYmlCcDhkV2RVZTZ2bUJsWWhIS2dhWGRRIiwieSI6InZPR1A2cVVZMUt3cFk3VFpENzI5c2p3S2d0b0hPZ1NyY1pHMmFUSExQYWsifSwicHVycG9zZSI6WyJnZW5lcmFsIl19XX19XX0 -i
Would return
{
"@context":"https://www.w3.org/ns/did-resolution/v1",
"didDocument":{
"id":"id:ion:test:EiC0wVS3K9VLv-XZVL55i8Y4d1Hk1kJpeckfDXIu0nM17A",
"@context":[
"https://www.w3.org/ns/did/v1",
{
"@base":"did:ion:test:EiC0wVS3K9VLv-XZVL55i8Y4d1Hk1kJpeckfDXIu0nM17A"
}
]
},
"methodMetadata":{
"recoveryCommitment":"EiDfGi474ij49Vq_yiKG1q8oC1jH_BFLfH27BIQKUnTBTw",
"anchored":false,
"initialState":"eyJkZWx0YV9oYXNoIjoiRWlDUlRKZ2Q0U0V2YUZDLW9fNUZjQnZJUkRtWF94Z3RLX3gwV0gtZXVhNGhrZyIsInJlY292ZXJ5X2NvbW1pdG1lbnQiOiJFaURmR2k0NzRpajQ5VnFfeWlLRzFxOG9DMWpIX0JGTGZIMjdCSVFLVW5UQlR3In0.eyJ1cGRhdGVfY29tbWl0bWVudCI6eyIwIjoxOCwiMSI6MzIsIjIiOjIyMywiMyI6MjYsIjQiOjQ2LCI1Ijo1OSwiNiI6MjI2LCI3Ijo0MCwiOCI6MjQ4LCI5IjoyNDUsIjEwIjo5MCwiMTEiOjE5MSwiMTIiOjIwMiwiMTMiOjM0LCIxNCI6MTM0LCIxNSI6MjE0LCIxNiI6MTc1LCIxNyI6NDAsIjE4IjoxMSwiMTkiOjg4LCIyMCI6MTk5LCIyMSI6MjUyLCIyMiI6MTcsIjIzIjo3NSwiMjQiOjEyNCwiMjUiOjEyNSwiMjYiOjE4NywiMjciOjQsIjI4IjoxMzIsIjI5IjoxMCwiMzAiOjgyLCIzMSI6MTE2LCIzMiI6MTkzLCIzMyI6Nzl9LCJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljX2tleXMiOlt7ImlkIjoielEzc2hta2VNZmh1RHdOOGExS3YiLCJ0eXBlIjoiU2VjcDI1NmsxVmVyaWZpY2F0aW9uS2V5MjAxOCIsImp3ayI6eyJrdHkiOiJFQyIsImNydiI6InNlY3AyNTZrMSIsIngiOiJhYTZhNDVhM1lnRW1sYks2cjVwYmlCcDhkV2RVZTZ2bUJsWWhIS2dhWGRRIiwieSI6InZPR1A2cVVZMUt3cFk3VFpENzI5c2p3S2d0b0hPZ1NyY1pHMmFUSExQYWsifSwicHVycG9zZSI6WyJnZW5lcmFsIl19XX19XX0"
}
}
And if the same query was made where the did was anchored, the result would instead be
{
"@context":"https://www.w3.org/ns/did-resolution/v1",
"didDocument":{
"id":"id:ion:test:EiC0wVS3K9VLv-XZVL55i8Y4d1Hk1kJpeckfDXIu0nM17A",
"@context":[
"https://www.w3.org/ns/did/v1",
{
"@base":"did:ion:test:EiC0wVS3K9VLv-XZVL55i8Y4d1Hk1kJpeckfDXIu0nM17A"
}
]
},
"methodMetadata":{
"recoveryCommitment":"EiDfGi474ij49Vq_yiKG1q8oC1jH_BFLfH27BIQKUnTBTw",
"anchored": true,
"initialState":"eyJkZWx0YV9oYXNoIjoiRWlDUlRKZ2Q0U0V2YUZDLW9fNUZjQnZJUkRtWF94Z3RLX3gwV0gtZXVhNGhrZyIsInJlY292ZXJ5X2NvbW1pdG1lbnQiOiJFaURmR2k0NzRpajQ5VnFfeWlLRzFxOG9DMWpIX0JGTGZIMjdCSVFLVW5UQlR3In0.eyJ1cGRhdGVfY29tbWl0bWVudCI6eyIwIjoxOCwiMSI6MzIsIjIiOjIyMywiMyI6MjYsIjQiOjQ2LCI1Ijo1OSwiNiI6MjI2LCI3Ijo0MCwiOCI6MjQ4LCI5IjoyNDUsIjEwIjo5MCwiMTEiOjE5MSwiMTIiOjIwMiwiMTMiOjM0LCIxNCI6MTM0LCIxNSI6MjE0LCIxNiI6MTc1LCIxNyI6NDAsIjE4IjoxMSwiMTkiOjg4LCIyMCI6MTk5LCIyMSI6MjUyLCIyMiI6MTcsIjIzIjo3NSwiMjQiOjEyNCwiMjUiOjEyNSwiMjYiOjE4NywiMjciOjQsIjI4IjoxMzIsIjI5IjoxMCwiMzAiOjgyLCIzMSI6MTE2LCIzMiI6MTkzLCIzMyI6Nzl9LCJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljX2tleXMiOlt7ImlkIjoielEzc2hta2VNZmh1RHdOOGExS3YiLCJ0eXBlIjoiU2VjcDI1NmsxVmVyaWZpY2F0aW9uS2V5MjAxOCIsImp3ayI6eyJrdHkiOiJFQyIsImNydiI6InNlY3AyNTZrMSIsIngiOiJhYTZhNDVhM1lnRW1sYks2cjVwYmlCcDhkV2RVZTZ2bUJsWWhIS2dhWGRRIiwieSI6InZPR1A2cVVZMUt3cFk3VFpENzI5c2p3S2d0b0hPZ1NyY1pHMmFUSExQYWsifSwicHVycG9zZSI6WyJnZW5lcmFsIl19XX19XX0"
}
}
I agree that the resolved DID Doc should contain the long-form ID when the resolver knows that it's not anchored. If the initial state is in some metadata returned by the resolver then it's up to the caller to append that initial state to the DID otherwise like Daniel said it could be unresolvable by the next party who gets the DID.
@JaceHensley as the subject of a DID if created a DID and I want someone to be able to resolve it prior to successful anchoring I am going to have to be responsible for that anyway in how I communicate my DID (i.e I will need to append the initial-state param), I don't see how it being appended to the id element in the did document facilitates this?
Put another way, as a client performing resolution I had to have the initial state to construct the resolution request in first place, so what utility does it serve sending it back to me in all areas of the did document where the id is quoted.
Following that logic then the create operation response for a sidetree node should then also return a full didDocument including the initial state appended in the id element (just to be clear I'm not proposing this as a solution just using it to highlight the inconsistency in behaviour)
Just to clarify the behaviour in case there is confusion, if I launched the following request at a sidetree node
curl http://localhost:3000/identifiers/did:ion:test:EiC0wVS3K9VLv-XZVL55i8Y4d1Hk1kJpeckfDXIu0nM17A
Even if the node has a pending registration request (i.e its waiting to close the batch and anchor the DID did:ion:test:EiC0wVS3K9VLv-XZVL55i8Y4d1Hk1kJpeckfDXIu0nM17A
), the sidetree node will return a 404
until the anchoring is successful. So in short unless I have constructed a resolution request that features initial state param I will not get back an initial state did document.
To further the point as to why it would be cleaner to shift this information into the method metadata, how am I as a resolver client able to quickly discern if a did has now been registered? Currently if I had launched a resolution request that featured the initial-state to guarantee some form of resolution, the only way I would know the did is now anchored is by inspecting the id of the did document and parsing to determine whether the initial-state param is present? Would it not be cleaner for a client to just check response.methodMetadata.anchored
?
@tplooker @peacekeeper @csuwildcat as discussed on id&d call today.... the did document id
MUST be a DID and NOT an DID_URL.... this means we need to return initial state in resolver meta data, and drop the query parameters... essentially, exactly what tobias suggested...
{
"@context":"https://www.w3.org/ns/did-resolution/v1",
"didDocument":{
"id":"id:ion:test:EiC0wVS3K9VLv-XZVL55i8Y4d1Hk1kJpeckfDXIu0nM17A",
"@context":[
"https://www.w3.org/ns/did/v1",
{
"@base":"did:ion:test:EiC0wVS3K9VLv-XZVL55i8Y4d1Hk1kJpeckfDXIu0nM17A"
}
]
},
"methodMetadata":{
"recoveryCommitment":"EiDfGi474ij49Vq_yiKG1q8oC1jH_BFLfH27BIQKUnTBTw",
"published": true,
"initialState":"eyJkZWx0YV9oYXNoIjoiRWlDUlRKZ2Q0U0V2YUZDLW9fNUZjQnZJUkRtWF94Z3RLX3gwV0gtZXVhNGhrZyIsInJlY292ZXJ5X2NvbW1pdG1lbnQiOiJFaURmR2k0NzRpajQ5VnFfeWlLRzFxOG9DMWpIX0JGTGZIMjdCSVFLVW5UQlR3In0.eyJ1cGRhdGVfY29tbWl0bWVudCI6eyIwIjoxOCwiMSI6MzIsIjIiOjIyMywiMyI6MjYsIjQiOjQ2LCI1Ijo1OSwiNiI6MjI2LCI3Ijo0MCwiOCI6MjQ4LCI5IjoyNDUsIjEwIjo5MCwiMTEiOjE5MSwiMTIiOjIwMiwiMTMiOjM0LCIxNCI6MTM0LCIxNSI6MjE0LCIxNiI6MTc1LCIxNyI6NDAsIjE4IjoxMSwiMTkiOjg4LCIyMCI6MTk5LCIyMSI6MjUyLCIyMiI6MTcsIjIzIjo3NSwiMjQiOjEyNCwiMjUiOjEyNSwiMjYiOjE4NywiMjciOjQsIjI4IjoxMzIsIjI5IjoxMCwiMzAiOjgyLCIzMSI6MTE2LCIzMiI6MTkzLCIzMyI6Nzl9LCJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljX2tleXMiOlt7ImlkIjoielEzc2hta2VNZmh1RHdOOGExS3YiLCJ0eXBlIjoiU2VjcDI1NmsxVmVyaWZpY2F0aW9uS2V5MjAxOCIsImp3ayI6eyJrdHkiOiJFQyIsImNydiI6InNlY3AyNTZrMSIsIngiOiJhYTZhNDVhM1lnRW1sYks2cjVwYmlCcDhkV2RVZTZ2bUJsWWhIS2dhWGRRIiwieSI6InZPR1A2cVVZMUt3cFk3VFpENzI5c2p3S2d0b0hPZ1NyY1pHMmFUSExQYWsifSwicHVycG9zZSI6WyJnZW5lcmFsIl19XX19XX0"
}
}
So I hit an issue with this and using https://github.com/digitalbazaar/jsonld-signatures. The problem comes from this framing.
So controller
in my case was the VC.issuer
field, which needs to be resolvable, but then it is also used to frame the resolved document to ensure that the controllerId
matches the resolved document's id. But the resolved DID doc's id wouldn't match the controller because the id
doesn't contain the initial-state param. I was able to work around this by resolving the controller myself and passing it as the controller when constructing the proof purpose class.
But this would also have been a problem if the controller
had the initial-state param and then since the VC had been issued the DID got anchored. In that case the resolved DID Doc wouldn't contain the initial-state param in the id
because the resolver would know that it was anchored. So maybe it's just not the best API for jsonld-signatures
to have?
Can someone please explain to me how you would handle the following use cases without having to have special method-specific code?
Please clarify how we can do this without requiring some sort of additional standard on how to relay long-form IDs in these scenarios, across all the various situations where someone would need to have the long-form in hand to proceed.
@csuwildcat similar to how you must handle fragment ids like did:example:123#fragment-id-456
... you are responsible for knowing when you need the full URI... for example, if you are using a documentLoader or other linked data processing....
You MUST write some JSON-LD tests to confirm that your proposed solutions will work and be interoperable for both LD / ZKP and JWT VCs, otherwise... you are just asking to implement things that are compatibility breaking.
Sounds like @JaceHensley may have written some tests that do this already, would you be willing to explain them to daniel?
Does the holder DID on a VC need to be resolvable? But in either case I agree that it's the DID holder's responsibility to provide a DID for the issuer to use. So it's their responsibility to provide a valid one.
2.
Which DID is the verifier trying to resolve here?
If it's the issuer's DID then IMO the issuer would be responsible for making sure the DID they put in the VC is resolvable.
If it's the holder's DID I'm not sure that's VC.holder needs to be resolvable but if so then my answer would be the same as 1
.
But the holder DID on the VP should be resolvable the same way an issuer is responsible for providing a resolvable DID when they issue VCs. But to make sure that the issuer of the VP is the same as the holder of the VC I do: parse(VC.holder.id).did === parse(VP.holder.id).did
, just comparing the unique suffixes essentially.
And then to make sure that the VP.holder has the authentication
to sign VPs the VP.holder is resolved and VP.verificationMethod is checked against the DID Doc. So VP.verificationMethod would be the short form DID with the #
(did:elem:123#primary). Like I mentioned about there is a caveat when the DIDDoc.id doesn't match the controller
in jsonld-signatures, and you have to resolve the DID yourself and pass that as the controller to the proof purpose. But that would the case even if the DID Doc only contained the initial-state param when the DID was anchored. (basically if the VC was issued before the DID was anchored and then verified after the DID got anchored you would hit the same problem of having to supply the controller yourself).
As far as tests I do have some but they don't focus on this exactly. For now we aren't anchoring our Element DIDs so the controller
is the long form DID and keyId
is the short form DID, and then the DID Doc contains just short form DIDs. So that's why I need to provide the resolved controller to the proof purpose class.
const signed = await jsigs.sign(
vc,
suite: new Suite({
controller: 'did:elem:EiBOH3jRdJZmRE4ew_lKc0RgSDsZphs3ddXmz2MHfKHXcQ;elem:initial-state=...',
keyId: 'did:elem:EiBOH3jRdJZmRE4ew_lKc0RgSDsZphs3ddXmz2MHfKHXcQ#primary',
privateKey: ...,
}),
documentLoader: testDocumentLoader,
purpose: new AssertionProofPurpose(),
compactProof: false,
)
const controllerDIDDoc = resolveDID(signed.issuer)
const result = await jsigs.verify(
signed,
suite: new Suite({
controller: signed.issuer,
keyId: signed.proof.verificationMethod,
// Get the public key for the provided verificationMethod
publicKey: findPublicKey(controllerDIDDoc, signed.proof.verificationMethod),
}),
documentLoader: testDocumentLoader,
// This by passes jsonld-signatures' framing that wouldn't match because DIDDoc.id doesn't match Suite.controller.
// This would be needed in any case where the resolvable DID URL is not equal to to the DID id in the DID Doc
purpose: new AssertionProofPurpose({controller: controllerDIDDoc}),
compactProof: false,
)
@JaceHensley you might want to use did key, the structure of the sidetree spec has changed a lot since we released the element testnet...
I'm sorry in advance for the breaking changes we are gonna give you with element spec v0.1.0 conformance release.
With the move to a fully qualified DID URI for long-form, this is no longer applicable, and inherently resolved.
The example request of
is returning
When I would expect to see the result as
Also can we get clarity of when sidetree will support the general query parameter of
initial-state
?