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

feat: Add Resource dereferencing #14

Merged
merged 21 commits into from
Jul 14, 2022
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
20 changes: 11 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ module github.com/cheqd/did-resolver
go 1.17

require (
github.com/cheqd/cheqd-node v0.5.0
github.com/cheqd/cheqd-node v0.6.0
github.com/iancoleman/orderedmap v0.2.0
github.com/labstack/echo/v4 v4.7.2
github.com/rs/zerolog v1.23.0
github.com/spf13/cobra v1.2.1
github.com/spf13/cobra v1.5.0
github.com/spf13/viper v1.11.0
github.com/stretchr/testify v1.7.1
google.golang.org/grpc v1.45.0
google.golang.org/grpc v1.46.2
google.golang.org/protobuf v1.28.0
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
gopkg.in/yaml.v3 v3.0.1
)

require (
Expand All @@ -25,7 +26,7 @@ require (
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/confio/ics23/go v0.7.0 // indirect
github.com/cosmos/btcutil v1.0.4 // indirect
github.com/cosmos/cosmos-sdk v0.45.1 // indirect
github.com/cosmos/cosmos-sdk v0.45.5 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d // indirect
github.com/dgraph-io/badger/v2 v2.2007.2 // indirect
Expand All @@ -40,6 +41,7 @@ require (
github.com/goccy/go-json v0.9.4 // indirect
github.com/golang/snappy v0.0.3 // indirect
github.com/google/btree v1.0.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/gtank/merlin v0.1.1 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
Expand Down Expand Up @@ -67,9 +69,9 @@ require (
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.11.0 // indirect
github.com/prometheus/client_golang v1.12.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.30.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/sasha-s/go-deadlock v0.2.1-0.20190427202633-1595213edefa // indirect
github.com/spf13/afero v1.8.2 // indirect
Expand All @@ -80,10 +82,10 @@ require (
github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca // indirect
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect
github.com/tendermint/go-amino v0.16.0 // indirect
github.com/tendermint/tendermint v0.34.15 // indirect
github.com/tendermint/tendermint v0.34.19 // indirect
github.com/tendermint/tm-db v0.6.6 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac // indirect
google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect
gopkg.in/ini.v1 v1.66.4 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
Expand Down
75 changes: 73 additions & 2 deletions go.sum

Large diffs are not rendered by default.

89 changes: 66 additions & 23 deletions services/diddoc_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import (
"strings"

cheqd "github.com/cheqd/cheqd-node/x/cheqd/types"
resource "github.com/cheqd/cheqd-node/x/resource/types"
"github.com/cheqd/did-resolver/types"
"github.com/golang/protobuf/jsonpb" //nolint
"github.com/iancoleman/orderedmap"
"google.golang.org/protobuf/runtime/protoiface"
)

Expand Down Expand Up @@ -41,12 +43,15 @@ func (ds DIDDocService) MarshallDID(didDoc cheqd.Did) (string, error) {
if err != nil {
return "", err
}
mapDID[verificationMethod] = formatedVerificationMethod
mapDID.Set(verificationMethod, json.RawMessage(formatedVerificationMethod))

// Context changes
if val, ok := mapDID[didContext]; ok {
mapDID["@"+didContext] = val
delete(mapDID, didContext)
if val, ok := mapDID.Get(didContext); ok {
mapDID.Set("@"+didContext, val)
mapDID.Delete(didContext)
mapDID.Sort(func(a *orderedmap.Pair, b *orderedmap.Pair) bool {
return a.Key() == "@"+didContext
})
}

result, err := json.Marshal(mapDID)
Expand All @@ -57,22 +62,56 @@ func (ds DIDDocService) MarshallDID(didDoc cheqd.Did) (string, error) {
}

func (ds DIDDocService) MarshallContentStream(contentStream protoiface.MessageV1, contentType types.ContentType) (string, error) {
var mapContent map[string]interface{}
var mapContent orderedmap.OrderedMap
var err error
var context types.ContentType
if contentType == types.DIDJSONLD || contentType == types.JSONLD {
context = types.DIDSchemaJSONLD
}

// VerKey changes, marshal
if verificationMethod, ok := contentStream.(*cheqd.VerificationMethod); ok {
mapContent, err = ds.prepareJWKPubkey(verificationMethod)
} else {
switch contentStream := contentStream.(type) {
case *cheqd.VerificationMethod:
mapContent, err = ds.prepareJWKPubkey(contentStream)
case *cheqd.Did:
contentStream.Context = []string{string(context)}
jsonDid, err := ds.MarshallDID(*contentStream)
if err != nil {
return "", err
}
return string(jsonDid), nil
case *resource.Resource:
dResource := types.DereferencedResource{
Context: []string{string(context)},
CollectionId: contentStream.Header.CollectionId,
Id: contentStream.Header.Id,
Name: contentStream.Header.Name,
ResourceType: contentStream.Header.ResourceType,
MediaType: contentStream.Header.MediaType,
Created: contentStream.Header.Created,
Checksum: contentStream.Header.Checksum,
PreviousVersionId: contentStream.Header.PreviousVersionId,
NextVersionId: contentStream.Header.NextVersionId,
Data: contentStream.Data,
}
jsonResource, err := json.Marshal(dResource)
if err != nil {
return "", err
}
return string(jsonResource), nil
default:
mapContent, err = ds.protoToMap(contentStream)
}

if err != nil {
return "", err
}

// Context changes
if contentType == types.DIDJSONLD || contentType == types.JSONLD {
mapContent["@"+didContext] = types.DIDSchemaJSONLD
if context != "" {
mapContent.Set("@"+didContext, context)
mapContent.Sort(func(a *orderedmap.Pair, b *orderedmap.Pair) bool {
return a.Key() == "@"+didContext
})
}

result, err := json.Marshal(mapContent)
Expand All @@ -97,40 +136,44 @@ func (DIDDocService) GetDIDFragment(fragmentId string, didDoc cheqd.Did) protoif
return nil
}

func (ds DIDDocService) prepareJWKPubkey(verificationMethod *cheqd.VerificationMethod) (map[string]interface{}, error) {
func (ds DIDDocService) prepareJWKPubkey(verificationMethod *cheqd.VerificationMethod) (orderedmap.OrderedMap, error) {
methodJson, err := ds.protoToMap(verificationMethod)
if err != nil {
return nil, err
return *orderedmap.New(), err
}
if len(verificationMethod.PublicKeyJwk) > 0 {
methodJson[publicKeyJwk] = cheqd.PubKeyJWKToMap(verificationMethod.PublicKeyJwk)
jsonKey, err := cheqd.PubKeyJWKToJson(verificationMethod.PublicKeyJwk)
if err != nil {
return *orderedmap.New(), err
}
methodJson.Set(publicKeyJwk, json.RawMessage(jsonKey))
}
return methodJson, nil
}

func (ds DIDDocService) MarshallVerificationMethod(verificationMethod []*cheqd.VerificationMethod) ([]map[string]interface{}, error) {
var verMethodList []map[string]interface{}
func (ds DIDDocService) MarshallVerificationMethod(verificationMethod []*cheqd.VerificationMethod) ([]byte, error) {
var verMethodList []orderedmap.OrderedMap
for _, value := range verificationMethod {
methodJson, err := ds.prepareJWKPubkey(value)
if err != nil {
return nil, err
return []byte{}, err
}
verMethodList = append(verMethodList, methodJson)
}
return verMethodList, nil
return json.Marshal(verMethodList)
}

func (ds DIDDocService) protoToMap(protoObject protoiface.MessageV1) (map[string]interface{}, error) {
func (ds DIDDocService) protoToMap(protoObject protoiface.MessageV1) (orderedmap.OrderedMap, error) {
mapObj := orderedmap.New()
jsonObj, err := ds.MarshallProto(protoObject)
if err != nil {
return nil, err
return *mapObj, err
}
var mapObj map[string]interface{}

err = json.Unmarshal([]byte(jsonObj), &mapObj)
if err != nil {
return nil, err
return *mapObj, err
}

return mapObj, err
return *mapObj, err
}
5 changes: 2 additions & 3 deletions services/diddoc_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,10 @@ func TestMarshallDID(t *testing.T) {
VerificationMethod: []*cheqd.VerificationMethod{&verificationMethod1, &verificationMethod2},
}

expectedDID := "{\"@context\":[\"test\"],\"id\":\"did:cheqd:mainnet:N22KY2Dyvmuu2PyyqSFKue\",\"verificationMethod\":[{\"controller\":\"did:cheqd:mainnet:N22KY2Dyvmuu2PyyqSFKue\",\"id\":\"did:cheqd:mainnet:N22KY2Dyvmuu2PyyqSFKue#verkey\",\"publicKeyMultibase\":\"zAKJP3f7BD6W4iWEQ9jwndVTCBq8ua2Utt8EEjJ6Vxsf\",\"type\":\"Ed25519VerificationKey2020\"},{\"controller\":\"did:cheqd:mainnet:N22KY2Dyvmuu2PyyqSFKue\",\"id\":\"did:cheqd:mainnet:N22KY2Dyvmuu2PyyqSFKue#verkey\",\"publicKeyJwk\":{\"crv\":\"Ed25519\",\"kty\":\"OKP\",\"x\":\"VCpo2LMLhn6iWku8MKvSLg2ZAoC-nlOyPVQaO3FxVeQ\"},\"type\":\"JsonWebKey2020\"}]}"

expectedDID := "{\"@context\":[\"test\"],\"id\":\"did:cheqd:mainnet:N22KY2Dyvmuu2PyyqSFKue\",\"verificationMethod\":[{\"id\":\"did:cheqd:mainnet:N22KY2Dyvmuu2PyyqSFKue#verkey\",\"type\":\"Ed25519VerificationKey2020\",\"controller\":\"did:cheqd:mainnet:N22KY2Dyvmuu2PyyqSFKue\",\"publicKeyMultibase\":\"zAKJP3f7BD6W4iWEQ9jwndVTCBq8ua2Utt8EEjJ6Vxsf\"},{\"id\":\"did:cheqd:mainnet:N22KY2Dyvmuu2PyyqSFKue#verkey\",\"type\":\"JsonWebKey2020\",\"controller\":\"did:cheqd:mainnet:N22KY2Dyvmuu2PyyqSFKue\",\"publicKeyJwk\":{\"crv\":\"Ed25519\",\"kty\":\"OKP\",\"x\":\"VCpo2LMLhn6iWku8MKvSLg2ZAoC-nlOyPVQaO3FxVeQ\"}}]}"
jsonDID, err := didDocService.MarshallDID(didDoc)

fmt.Println(jsonDID)
require.EqualValues(t, jsonDID, expectedDID)
require.EqualValues(t, expectedDID, jsonDID)
require.Empty(t, err)
}
48 changes: 40 additions & 8 deletions services/ledger_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ import (

cheqd "github.com/cheqd/cheqd-node/x/cheqd/types"
cheqdUtils "github.com/cheqd/cheqd-node/x/cheqd/utils"
resource "github.com/cheqd/cheqd-node/x/resource/types"
"github.com/rs/zerolog/log"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)

type LedgerServiceI interface {
QueryDIDDoc(did string) (cheqd.Did, cheqd.Metadata, bool, error)
QueryResource(collectionDid string, resourceId string) (resource.Resource, bool, error)
GetNamespaces() []string
}

Expand All @@ -43,20 +45,13 @@ func (ls LedgerService) QueryDIDDoc(did string) (cheqd.Did, cheqd.Metadata, bool
return cheqd.Did{}, cheqd.Metadata{}, false, fmt.Errorf("namespace not supported: %s", namespace)
}

log.Info().Msgf("Connecting to the ledger: %s", serverAddr)
conn, err := ls.openGRPCConnection(serverAddr)
if err != nil {
log.Error().Err(err).Msg("QueryDIDDoc: failed connection")
return cheqd.Did{}, cheqd.Metadata{}, false, err
}

defer func(conn *grpc.ClientConn) {
err := conn.Close()
if err != nil {
log.Panic().Err(err).Msg("QueryDIDDoc: failed to close connection")
panic(err)
}
}(conn)
defer mustCloseGRPCConnection(conn)

log.Info().Msgf("Querying did doc: %s", did)
client := cheqd.NewQueryClient(conn)
Expand All @@ -68,6 +63,32 @@ func (ls LedgerService) QueryDIDDoc(did string) (cheqd.Did, cheqd.Metadata, bool
return *didDocResponse.Did, *didDocResponse.Metadata, true, err
}

func (ls LedgerService) QueryResource(did string, resourceId string) (resource.Resource, bool, error) {
collectionId, namespace, _, _ := cheqdUtils.TrySplitDID(did)
serverAddr, namespaceFound := ls.ledgers[namespace]
if !namespaceFound {
return resource.Resource{}, false, fmt.Errorf("namespace not supported: %s", namespace)
}

conn, err := ls.openGRPCConnection(serverAddr)
if err != nil {
log.Error().Err(err).Msg("QueryResource: failed connection")
return resource.Resource{}, false, err
}

defer mustCloseGRPCConnection(conn)

log.Info().Msgf("Querying did resource: %s, %s", did, resourceId)

client := resource.NewQueryClient(conn)
resourceResponse, err := client.Resource(context.Background(), &resource.QueryGetResourceRequest{CollectionId: collectionId, Id: resourceId})
if err != nil {
return resource.Resource{}, false, nil
}

return *resourceResponse.Resource, true, err
}

func (ls *LedgerService) RegisterLedger(namespace string, url string) error {
if namespace == "" {
err := errors.New("namespace cannot be empty")
Expand Down Expand Up @@ -109,6 +130,17 @@ func (ls LedgerService) openGRPCConnection(addr string) (conn *grpc.ClientConn,
return conn, nil
}

func mustCloseGRPCConnection(conn *grpc.ClientConn) {
if conn == nil {
return
}
err := conn.Close()
if err != nil {
log.Panic().Err(err).Msg("QueryDIDDoc: failed to close connection")
panic(err)
}
}

func (ls LedgerService) GetNamespaces() []string {
keys := make([]string, 0, len(ls.ledgers))
for k := range ls.ledgers {
Expand Down
34 changes: 34 additions & 0 deletions services/ledger_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"time"

cheqd "github.com/cheqd/cheqd-node/x/cheqd/types"
resource "github.com/cheqd/cheqd-node/x/resource/types"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -42,3 +43,36 @@ func TestQueryDIDDoc(t *testing.T) {
})
}
}

func TestQueryResource(t *testing.T) {
subtests := []struct {
name string
collectionDid string
resourceId string
expectedResource resource.Resource
expectedIsFound bool
expectedError error
}{
{
name: "DeadlineExceeded",
collectionDid: "321",
resourceId: "123",
expectedResource: resource.Resource{},
expectedIsFound: false,
expectedError: errors.New("namespace not supported: "),
},
}

for _, subtest := range subtests {
t.Run(subtest.name, func(t *testing.T) {
timeout, err := time.ParseDuration("5s")
require.NoError(t, err)

ledgerService := NewLedgerService(timeout, false)
resource, isFound, err := ledgerService.QueryResource(subtest.collectionDid, subtest.resourceId)
require.EqualValues(t, subtest.expectedResource, resource)
require.EqualValues(t, subtest.expectedIsFound, isFound)
require.EqualValues(t, subtest.expectedError, err)
})
}
}
Loading