Skip to content
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

Support ION DID Reconstruction & Long Form DID resolution #389

Merged
merged 11 commits into from
May 24, 2023
Merged

Conversation

decentralgabe
Copy link
Member

#349

adds the ability to reconstruct any ION DID's state from a set of patches

@codecov-commenter
Copy link

codecov-commenter commented May 22, 2023

Codecov Report

Merging #389 (601c509) into main (3c2456d) will increase coverage by 0.15%.
The diff coverage is 61.11%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #389      +/-   ##
==========================================
+ Coverage   57.18%   57.34%   +0.15%     
==========================================
  Files          65       67       +2     
  Lines        7016     7250     +234     
==========================================
+ Hits         4012     4157     +145     
- Misses       2290     2362      +72     
- Partials      714      731      +17     
Impacted Files Coverage Δ
did/ion/operations.go 50.30% <ø> (-2.31%) ⬇️
did/pkh/resolver.go 0.00% <0.00%> (ø)
did/web/resolver.go 0.00% <0.00%> (ø)
did/ion/model.go 43.00% <36.00%> (-19.96%) ⬇️
did/resolution/resolver.go 16.95% <50.00%> (ø)
did/ion/resolver.go 57.32% <57.32%> (ø)
did/ion/did.go 67.01% <73.94%> (+24.70%) ⬆️
did/ion/enum.go 100.00% <100.00%> (ø)
did/ion/request.go 62.15% <100.00%> (ø)
did/jwk/resolver.go 66.67% <100.00%> (ø)
... and 6 more

did/ion/did.go Outdated Show resolved Hide resolved
did/ion/did.go Outdated
@@ -85,3 +108,216 @@ func LongToShortFormDID(longFormDID string) (string, error) {
}
return shortFormDID, nil
}

// PatchesToDIDDocument applies a list of sidetree state patches in order resulting in a DID Document.
func PatchesToDIDDocument(shortFormDID, longFormDID string, patches []any) (*did.Document, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I strongly discourage the use of any unless there is a strong reason to do so. Can you clarify why it's preferable to have patches be []any?

An alternative is to define an interface that all patches implement.

type Patch interface{
  isPatch()
}

func (a AddServicesAction) isPatch() { }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will switch to interface

did/ion/did.go Outdated
Comment on lines 130 to 133
switch knownPatch.(type) {
case AddServicesAction:
addServicePatch := knownPatch.(AddServicesAction)
doc.Services = append(doc.Services, addServicePatch.Services...)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you do this, then you don't need to re-cast within each case. You can use typedPatch

Suggested change
switch knownPatch.(type) {
case AddServicesAction:
addServicePatch := knownPatch.(AddServicesAction)
doc.Services = append(doc.Services, addServicePatch.Services...)
switch typedPatch := knownPatch.(type) {
case AddServicesAction:
doc.Services = append(doc.Services, typedPatch.Services...)

did/ion/did.go Outdated

// tryCastPatch attempts to cast a patch to a known patch type
func tryCastPatch(patch any) (any, error) {
switch patch.(type) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above.

Mixing map[string]any and typed structs leads to too much freedom and code that is harder to maintain. Can this API be structured so we can have devs avoid shooting themselves in the foot?

did/ion/did.go Outdated
removed := false
for i, key := range doc.VerificationMethod {
if key.ID == id {
doc.VerificationMethod = append(doc.VerificationMethod[:i], doc.VerificationMethod[i+1:]...)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a lot of copying around within this function. Is this OK?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a way to remove an element in an array should be fine

did/ion/did.go Outdated
doc.AssertionMethod = append(doc.AssertionMethod[:j], doc.AssertionMethod[j+1:]...)
}
}
for j, auth := range doc.Authentication {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be KeyAgreement?

did/ion/did.go Outdated
doc.KeyAgreement = append(doc.KeyAgreement[:j], doc.KeyAgreement[j+1:]...)
}
}
for j, auth := range doc.Authentication {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CapabilityInvocation?

did/ion/did.go Outdated
doc.CapabilityInvocation = append(doc.CapabilityInvocation[:j], doc.CapabilityInvocation[j+1:]...)
}
}
for j, auth := range doc.Authentication {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CapabilityDelegation?

did/ion/did.go Outdated

// TODO(gabe): in the future handle the case where the value is not a simple ID
// remove from all other key lists
for j, auth := range doc.Authentication {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to un-nest this for loop and the following ones?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

doc, err := PatchesToDIDDocument("did:ion:test", "", []any{struct{ Bad string }{Bad: "bad"}})
assert.Empty(tt, doc)
assert.Error(tt, err)
assert.Contains(tt, err.Error(), "unknown patch type")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and elsewhere

Suggested change
assert.Contains(tt, err.Error(), "unknown patch type")
assert.ErrorContains(tt, err, "unknown patch type")

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

did/ion/did.go Outdated Show resolved Hide resolved
did/ion/did.go Outdated Show resolved Hide resolved
did/ion/did.go Outdated
doc.AssertionMethod = append(doc.AssertionMethod[:j], doc.AssertionMethod[j+1:]...)
}
}
for j, auth := range doc.Authentication {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is repeated. Is it intentional?

If not, should there be a test for this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

did/ion/did.go Outdated
doc.Authentication = append(doc.Authentication[:j], doc.Authentication[j+1:]...)
}
}
for j, auth := range doc.CapabilityInvocation {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for j, auth := range doc.CapabilityInvocation {
for j, ci := range doc.CapabilityInvocation {

did/ion/did.go Outdated
doc.CapabilityInvocation = append(doc.CapabilityInvocation[:j], doc.CapabilityInvocation[j+1:]...)
}
}
for j, auth := range doc.CapabilityDelegation {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for j, auth := range doc.CapabilityDelegation {
for j, cd := range doc.CapabilityDelegation {

// ResolutionOption https://www.w3.org/TR/did-spec-registries/#did-resolution-options
type ResolutionOption any
// Option https://www.w3.org/TR/did-spec-registries/#did-resolution-options
type Option any
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having any makes me sad :(

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can fix this when we implement any options
#331

if !ok {
return fmt.Errorf("patch has no action")
}
currPatchBytes, err := json.Marshal(currPatch)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is a way to use json.RawMessage to avoid the whole unmarshal -> marshal -> unmarshal round tripping, but I've never done it. If you're feeling brave :D

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm you're right but I am not feeling brave

did/ion/did.go Outdated

// TODO(gabe): in the future handle the case where the value is not a simple ID
// remove from all other key lists
for j, auth := range doc.Authentication {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also unnest this and the following for loops?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how? this needs to be nested as far as I can tell

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i have no idea what I was talking about... ignore please.

decentralgabe and others added 3 commits May 23, 2023 19:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants