Skip to content
This repository has been archived by the owner on Dec 12, 2024. It is now read-only.

Commit

Permalink
add context to resolver (#329)
Browse files Browse the repository at this point in the history
* add context to resolver

* lints

* pr comments
  • Loading branch information
decentralgabe authored Mar 28, 2023
1 parent e6d9b21 commit a9e587d
Show file tree
Hide file tree
Showing 13 changed files with 62 additions and 54 deletions.
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

// 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

0 comments on commit a9e587d

Please sign in to comment.