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

add context to resolver #329

Merged
merged 4 commits into from
Mar 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion did/ion/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ func NewIONResolver(client *http.Client, baseURL string) (*Resolver, error) {
}

// Resolve resolves a did:ion DID by appending the DID to the base URL with the identifiers path and making a GET request
func (i Resolver) Resolve(ctx context.Context, id string, _ did.ResolutionOptions) (*did.ResolutionResult, error) {
func (i Resolver) Resolve(ctx context.Context, id string, _ did.ResolutionOption) (*did.ResolutionResult, error) {
if i.baseURL.String() == "" {
return nil, errors.New("resolver URL cannot be empty")
}
Expand Down
10 changes: 5 additions & 5 deletions did/ion/operations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func TestResolver(t *testing.T) {
assert.NoError(tt, err)
assert.NotEmpty(tt, resolver)

result, err := resolver.Resolve(context.TODO(), "bad", nil)
result, err := resolver.Resolve(context.Background(), "bad", nil)
assert.Error(tt, err)
assert.Empty(tt, result)
assert.Contains(tt, err.Error(), "could not resolve DID")
Expand All @@ -65,7 +65,7 @@ func TestResolver(t *testing.T) {
assert.NoError(tt, err)
assert.NotEmpty(tt, resolver)

result, err := resolver.Resolve(context.TODO(), "did:ion:test", nil)
result, err := resolver.Resolve(context.Background(), "did:ion:test", nil)
assert.Error(tt, err)
assert.Empty(tt, result)
assert.Contains(tt, err.Error(), "could not parse DID Resolution Result or DID Document")
Expand All @@ -82,7 +82,7 @@ func TestResolver(t *testing.T) {
assert.NoError(tt, err)
assert.NotEmpty(tt, resolver)

result, err := resolver.Resolve(context.TODO(), "did:ion:test", nil)
result, err := resolver.Resolve(context.Background(), "did:ion:test", nil)
assert.NoError(tt, err)
assert.NotEmpty(tt, result)
assert.Equal(tt, "did:ion:test", result.Document.ID)
Expand All @@ -98,7 +98,7 @@ func TestResolver(t *testing.T) {
assert.NoError(tt, err)
assert.NotEmpty(tt, resolver)

err = resolver.Anchor(context.TODO(), nil)
err = resolver.Anchor(context.Background(), nil)
assert.Error(tt, err)
assert.Contains(tt, err.Error(), "anchor operation failed")
})
Expand Down Expand Up @@ -127,7 +127,7 @@ func TestResolver(t *testing.T) {
assert.NotEmpty(tt, did)
assert.NotEmpty(tt, createOp)

err = resolver.Anchor(context.TODO(), CreateRequest{
err = resolver.Anchor(context.Background(), CreateRequest{
Type: Create,
SuffixData: SuffixData{
DeltaHash: "deltaHash",
Expand Down
14 changes: 7 additions & 7 deletions did/key.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package did

import (
"context"
gocrypto "crypto"
"fmt"
"strings"
Expand All @@ -24,8 +25,8 @@ type (
)

const (
// DIDKeyPrefix did:key prefix
DIDKeyPrefix = "did:key"
// KeyPrefix did:key prefix
KeyPrefix = "did:key"
)

func (d DIDKey) IsValid() bool {
Expand All @@ -39,7 +40,7 @@ func (d DIDKey) String() string {

// Suffix returns the value without the `did:key` prefix
func (d DIDKey) Suffix() (string, error) {
split := strings.Split(string(d), DIDKeyPrefix+":")
split := strings.Split(string(d), KeyPrefix+":")
if len(split) != 2 {
return "", fmt.Errorf("invalid did:key: %s", d)
}
Expand Down Expand Up @@ -100,7 +101,7 @@ func CreateDIDKey(kt crypto.KeyType, publicKey []byte) (*DIDKey, error) {
if err != nil {
return nil, errors.Wrap(err, "could not encode did:key")
}
did := DIDKey(fmt.Sprintf("%s:%s", DIDKeyPrefix, encoded))
did := DIDKey(fmt.Sprintf("%s:%s", KeyPrefix, encoded))
return &did, nil
}

Expand Down Expand Up @@ -266,16 +267,15 @@ func GetSupportedDIDKeyTypes() []crypto.KeyType {

type KeyResolver struct{}

func (KeyResolver) Resolve(did string, _ ResolutionOptions) (*ResolutionResult, error) {
if !strings.HasPrefix(did, DIDKeyPrefix) {
func (KeyResolver) Resolve(_ context.Context, did string, _ ...ResolutionOption) (*ResolutionResult, error) {
if !strings.HasPrefix(did, KeyPrefix) {
return nil, fmt.Errorf("not a did:key DID: %s", did)
}
didKey := DIDKey(did)
doc, err := didKey.Expand()
if err != nil {
return nil, errors.Wrapf(err, "could not expand did:key DID: %s", did)
}
// TODO(gabe) full resolution support to be added in https://github.com/TBD54566975/ssi-sdk/issues/38
return &ResolutionResult{Document: *doc}, nil
}

Expand Down
5 changes: 3 additions & 2 deletions did/key_fuzz_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package did

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -33,7 +34,7 @@ func FuzzCreateAndResolve(f *testing.F) {
keytypes := GetSupportedDIDKeyTypes()
ktLen := len(keytypes)

resolvers := []Resolution{KeyResolver{}, WebResolver{}, PKHResolver{}, PeerResolver{}}
resolvers := []Resolver{KeyResolver{}, WebResolver{}, PKHResolver{}, PeerResolver{}}
resolver, _ := NewResolver(resolvers...)

for i, pk := range mockPubKeys {
Expand All @@ -46,7 +47,7 @@ func FuzzCreateAndResolve(f *testing.F) {
didKey, err := CreateDIDKey(kt, pubKey)
assert.NoError(t, err)

doc, err := resolver.Resolve(didKey.String())
doc, err := resolver.Resolve(context.Background(), didKey.String())
if err != nil {
t.Skip()
}
Expand Down
5 changes: 3 additions & 2 deletions did/key_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package did

import (
"context"
gocrypto "crypto"
"crypto/ecdsa"
"crypto/ed25519"
Expand Down Expand Up @@ -213,14 +214,14 @@ func TestGenerateAndDecode(t *testing.T) {
}

func TestGenerateAndResolve(t *testing.T) {
resolvers := []Resolution{KeyResolver{}, WebResolver{}, PKHResolver{}, PeerResolver{}}
resolvers := []Resolver{KeyResolver{}, WebResolver{}, PKHResolver{}, PeerResolver{}}
resolver, _ := NewResolver(resolvers...)

for _, kt := range GetSupportedDIDKeyTypes() {
_, didKey, err := GenerateDIDKey(kt)
assert.NoError(t, err)

doc, err := resolver.Resolve(didKey.String())
doc, err := resolver.Resolve(context.Background(), didKey.String())
assert.NoError(t, err)
assert.NotEmpty(t, doc)
assert.Equal(t, didKey.String(), doc.Document.ID)
Expand Down
11 changes: 6 additions & 5 deletions did/peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package did

import (
"context"
gocrypto "crypto"
b64 "encoding/base64"
"fmt"
Expand Down Expand Up @@ -190,8 +191,8 @@ func (DIDPeer) IsValidPurpose(p PurposeType) bool {

// Resolve resolves a did:peer into a DID Document
// To do so, it decodes the key, constructs a verification method, and returns a DID Document .This allows PeerMethod0
// to implement the DID Resolution interface and be used to expand the did into the DID Document.
func (PeerMethod0) resolve(did DID, _ ResolutionOptions) (*ResolutionResult, error) {
// to implement the DID Resolver interface and be used to expand the did into the DID Document.
func (PeerMethod0) resolve(did DID, _ ResolutionOption) (*ResolutionResult, error) {
d, ok := did.(DIDPeer)
if !ok {
return nil, errors.Wrap(util.CastingError, "did:peer")
Expand Down Expand Up @@ -231,7 +232,7 @@ func (PeerMethod0) resolve(did DID, _ ResolutionOptions) (*ResolutionResult, err
return &ResolutionResult{Document: document}, nil
}

func (PeerMethod1) resolve(d DID, _ ResolutionOptions) (*ResolutionResult, error) {
func (PeerMethod1) resolve(d DID, _ ResolutionOption) (*ResolutionResult, error) {
if _, ok := d.(DIDPeer); !ok {
return nil, errors.Wrap(util.CastingError, DIDPeerPrefix)
}
Expand All @@ -256,7 +257,7 @@ func (DIDPeer) buildVerificationMethod(data, did string) (*VerificationMethod, e
// Resolve Splits the DID string into element.
// Extract element purpose and decode each key or service.
// Insert each key or service into the document according to the designated pu
func (PeerMethod2) resolve(did DID, _ ResolutionOptions) (*ResolutionResult, error) {
func (PeerMethod2) resolve(did DID, _ ResolutionOption) (*ResolutionResult, error) {
d, ok := did.(DIDPeer)
if !ok {
return nil, errors.Wrap(util.CastingError, "did:peer")
Expand Down Expand Up @@ -512,7 +513,7 @@ func peerMethodAvailable(m string) bool {

type PeerResolver struct{}

func (PeerResolver) Resolve(did string, opts ResolutionOptions) (*ResolutionResult, error) {
func (PeerResolver) Resolve(_ context.Context, did string, opts ...ResolutionOption) (*ResolutionResult, error) {
if !strings.HasPrefix(did, DIDPeerPrefix) {
return nil, fmt.Errorf("not a did:peer DID: %s", did)
}
Expand Down
13 changes: 7 additions & 6 deletions did/peer_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package did

import (
"context"
"testing"

"github.com/TBD54566975/ssi-sdk/crypto"
Expand Down Expand Up @@ -118,30 +119,30 @@ func TestDIDPeerUtilities(t *testing.T) {
func TestPeerResolver(t *testing.T) {
bad := "asdf"
var r PeerResolver
_, err := r.Resolve(bad, nil)
_, err := r.Resolve(context.Background(), bad, nil)
assert.Error(t, err)

m0 := "did:peer:0z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH"
_, err = r.Resolve(m0, nil)
_, err = r.Resolve(context.Background(), m0, nil)
assert.NoError(t, err)

mbad := "did:peer:4z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH"
_, err = r.Resolve(mbad, nil)
_, err = r.Resolve(context.Background(), mbad, nil)
assert.Error(t, err)

// https://identity.foundation/peer-did-method-spec/#multi-key-creation - key agreement
m2 := "did:peer:2.Ez6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0="
_, err = r.Resolve(m2, nil)
_, err = r.Resolve(context.Background(), m2, nil)
assert.NoError(t, err)

// https://identity.foundation/peer-did-method-spec/#multi-key-creation w/ key agreement
// We currently don't support key agreement, so should throw error
m2 = "did:peer:2.Ez6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH.VzXwpBnMdCm1cLmKuzgESn29nqnonp1ioqrQMRHNsmjMyppzx8xB2pv7cw8q1PdDacSrdWE3dtB9f7Nxk886mdzNFoPtY.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInIiOlsiZGlkOmV4YW1wbGU6c29tZW1lZGlhdG9yI3NvbWVrZXkiXSwiYSI6WyJkaWRjb21tL3YyIiwiZGlkY29tbS9haXAyO2Vudj1yZmM1ODciXX0="
_, err = r.Resolve(m2, nil)
_, err = r.Resolve(context.Background(), m2, nil)
assert.NoError(t, err)

m1 := "did:peer:1z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH"
_, err = r.Resolve(m1, nil)
_, err = r.Resolve(context.Background(), m1, nil)
assert.Error(t, err)
}

Expand Down
4 changes: 2 additions & 2 deletions did/pkh.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package did

import (
"context"
"embed"
"fmt"
"regexp"
Expand Down Expand Up @@ -246,7 +247,7 @@ func IsValidPKH(did DIDPKH) bool {

type PKHResolver struct{}

func (PKHResolver) Resolve(did string, _ ResolutionOptions) (*ResolutionResult, error) {
func (PKHResolver) Resolve(_ context.Context, did string, _ ...ResolutionOption) (*ResolutionResult, error) {
if !strings.HasPrefix(did, DIDPKHPrefix) {
return nil, fmt.Errorf("not a did:pkh DID: %s", did)
}
Expand All @@ -255,7 +256,6 @@ func (PKHResolver) Resolve(did string, _ ResolutionOptions) (*ResolutionResult,
if err != nil {
return nil, errors.Wrapf(err, "could not expand did:pkh DID: %s", did)
}
// TODO(gabe) full resolution support to be added in https://github.com/TBD54566975/ssi-sdk/issues/38
return &ResolutionResult{Document: *doc}, nil
}

Expand Down
31 changes: 16 additions & 15 deletions did/resolver.go
Original file line number Diff line number Diff line change
@@ -1,34 +1,35 @@
package did

import (
"context"
"fmt"
"strings"

"github.com/goccy/go-json"
"github.com/pkg/errors"
)

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

Choose a reason for hiding this comment

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

Whenever I see any, it's typically a symptom of an API that hasn't been fleshed out yet. In this case, it's unclear the shape of what ResolutionOption is acceptable by the functions that use it.

Instead, I would define an interface. Mind creating an issue to address?

Separately, This doesn't seem to be used anywhere.

Copy link
Member Author

Choose a reason for hiding this comment

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

yeah not used anywhere yet...will create an issue

Copy link
Member Author

Choose a reason for hiding this comment

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


// Resolution provides an interface for resolving DIDs as per the spec https://www.w3.org/TR/did-core/#did-resolution
type Resolution interface {
// Resolver provides an interface for resolving DIDs as per the spec https://www.w3.org/TR/did-core/#did-resolution
type Resolver interface {
// Resolve Attempts to resolve a DID for a given method
Resolve(did string, opts ResolutionOptions) (*ResolutionResult, error)
Resolve(ctx context.Context, did string, opts ...ResolutionOption) (*ResolutionResult, error)
// Method provides the method for the given resolution implementation
Method() Method
}

// Resolver resolves a DID. The current implementation ssk-sdk does not have a universal resolver:
// MultiMethodResolver resolves a DID. The current implementation ssk-sdk does not have a universal resolver:
// https://github.com/decentralized-identity/universal-resolver
// In its place, this method attempts to resolve DID methods that can be resolved without relying on additional services.
type Resolver struct {
resolvers map[Method]Resolution
type MultiMethodResolver struct {
resolvers map[Method]Resolver
methods []Method
}

func NewResolver(resolvers ...Resolution) (*Resolver, error) {
r := make(map[Method]Resolution)
func NewResolver(resolvers ...Resolver) (*MultiMethodResolver, error) {
r := make(map[Method]Resolver)
var methods []Method
for _, resolver := range resolvers {
method := resolver.Method()
Expand All @@ -38,22 +39,22 @@ func NewResolver(resolvers ...Resolution) (*Resolver, error) {
r[method] = resolver
methods = append(methods, method)
}
return &Resolver{resolvers: r, methods: methods}, nil
return &MultiMethodResolver{resolvers: r, methods: methods}, nil
}

// Resolve attempts to resolve a DID for a given method
func (dr Resolver) Resolve(did string, opts ...ResolutionOptions) (*ResolutionResult, error) {
func (dr MultiMethodResolver) Resolve(ctx context.Context, did string, opts ...ResolutionOption) (*ResolutionResult, error) {
method, err := GetMethodForDID(did)
if err != nil {
return nil, errors.Wrap(err, "failed to get method for DID before resolving")
}
if resolver, ok := dr.resolvers[method]; ok {
return resolver.Resolve(did, opts)
return resolver.Resolve(ctx, did, opts)
}
return nil, fmt.Errorf("unsupported method: %s", method)
}

func (dr Resolver) SupportedMethods() []Method {
func (dr MultiMethodResolver) SupportedMethods() []Method {
return dr.methods
}

Expand All @@ -72,7 +73,7 @@ func ParseDIDResolution(resolvedDID []byte) (*ResolutionResult, error) {
return nil, errors.New("cannot parse empty resolved DID")
}

// first try to parse as a DID Resolution Result
// first try to parse as a DID Resolver Result
var result ResolutionResult
if err := json.Unmarshal(resolvedDID, &result); err == nil {
if result.IsEmpty() {
Expand Down
Loading