Skip to content

Commit

Permalink
feat: VC Wallet Query By PresentationExchange
Browse files Browse the repository at this point in the history
- PresentationExchange query support
- GetAll by content type support
- Closes hyperledger-archives#2712
- Closes hyperledger-archives#2713

Signed-off-by: sudesh.shetty <sudesh.shetty@securekey.com>
  • Loading branch information
sudeshrshetty committed Apr 5, 2021
1 parent 17bb393 commit ea43db4
Show file tree
Hide file tree
Showing 11 changed files with 799 additions and 35 deletions.
46 changes: 33 additions & 13 deletions pkg/client/vcwallet/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,11 @@ func (c *Client) Close() bool {
// Returns exported locked wallet.
//
// Supported data models:
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Profile
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection
//
func (c *Client) Export(auth string) (json.RawMessage, error) {
// TODO to be added #2433
Expand All @@ -144,11 +147,12 @@ func (c *Client) Export(auth string) (json.RawMessage, error) {
// - auth: token used while exporting the wallet.
//
// Supported data models:
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Profile
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#CachedDIDDocument
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Key
//
func (c *Client) Import(auth string, contents json.RawMessage) error {
// TODO to be added #2433
Expand All @@ -158,11 +162,12 @@ func (c *Client) Import(auth string, contents json.RawMessage) error {
// Add adds given data model to wallet contents store.
//
// Supported data models:
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Profile
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#CachedDIDDocument
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Key
//
// TODO: (#2433) support for correlation between wallet contents (ex: credentials to a profile/collection).
func (c *Client) Add(contentType wallet.ContentType, content json.RawMessage) error {
Expand All @@ -177,9 +182,9 @@ func (c *Client) Add(contentType wallet.ContentType, content json.RawMessage) er
// Remove removes wallet content by content ID.
//
// Supported data models:
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Profile
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#CachedDIDDocument
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection
//
Expand All @@ -190,25 +195,40 @@ func (c *Client) Remove(contentType wallet.ContentType, contentID string) error
// Get fetches a wallet content by content ID.
//
// Supported data models:
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Profile
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#CachedDIDDocument
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection
//
func (c *Client) Get(contentType wallet.ContentType, contentID string) (json.RawMessage, error) {
return c.wallet.Get(contentType, contentID)
}

// Query returns a collection of results based on current wallet contents.
// GetAll fetches all wallet contents of given type.
//
// Supported data models:
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#Credential
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#DIDResolutionResponse
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#meta-data
// - https://w3c-ccg.github.io/universal-wallet-interop-spec/#connection
//
func (c *Client) GetAll(contentType wallet.ContentType) ([]json.RawMessage, error) {
return c.wallet.GetAll(contentType)
}

// Query runs query against wallet credential contents and returns presentation containing credential results.
//
// https://w3c-ccg.github.io/universal-wallet-interop-spec/#query
//
// Supported Query Types:
// - https://www.w3.org/TR/json-ld11-framing
// - https://identity.foundation/presentation-exchange
// - https://w3c-ccg.github.io/vp-request-spec/#query-by-example
//
func (c *Client) Query(query *wallet.QueryParams) ([]json.RawMessage, error) {
// TODO to be added #2433
return nil, fmt.Errorf("to be implemented")
func (c *Client) Query(params *wallet.QueryParams) (*verifiable.Presentation, error) {
return c.wallet.Query(params)
}

// Issue adds proof to a Verifiable Credential.
Expand Down
46 changes: 45 additions & 1 deletion pkg/client/vcwallet/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,42 @@ func TestClient_Get(t *testing.T) {
require.Equal(t, sampleContentValid, string(content))
}

func TestClient_GetAll(t *testing.T) {
const vcContent = `{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://www.w3.org/2018/credentials/examples/v1"
],
"id": "%s",
"issuer": {
"id": "did:example:76e12ec712ebc6f1c221ebfeb1f"
},
"type": [
"VerifiableCredential",
"UniversityDegreeCredential"
]
}`

mockctx := newMockProvider()
err := CreateProfile(sampleUserID, mockctx, wallet.WithPassphrase(samplePassPhrase))
require.NoError(t, err)

vcWalletClient, err := New(sampleUserID, mockctx, wallet.WithUnlockByPassphrase(samplePassPhrase))
require.NotEmpty(t, vcWalletClient)
require.NoError(t, err)

// save test data
const count = 5

for i := 0; i < count; i++ {
require.NoError(t, vcWalletClient.Add(wallet.Credential, []byte(fmt.Sprintf(vcContent, uuid.New().String()))))
}

vcs, err := vcWalletClient.GetAll(wallet.Credential)
require.NoError(t, err)
require.Len(t, vcs, count)
}

func TestClient_Remove(t *testing.T) {
mockctx := newMockProvider()
err := CreateProfile(sampleUserID, mockctx, wallet.WithKeyServerURL(sampleKeyServerURL))
Expand Down Expand Up @@ -714,7 +750,15 @@ func TestClient_Query(t *testing.T) {
require.NotEmpty(t, vcWalletClient)
require.NoError(t, err)

results, err := vcWalletClient.Query(&wallet.QueryParams{})
results, err := vcWalletClient.Query(&wallet.QueryParams{Type: "QueryByExample"})
require.Empty(t, results)
require.Error(t, err)
require.EqualError(t, err, "no result found")

require.NoError(t, vcWalletClient.Open(wallet.WithUnlockByPassphrase(samplePassPhrase)))
require.NoError(t, vcWalletClient.Add(wallet.Credential, []byte(sampleUDCVC)))

results, err = vcWalletClient.Query(&wallet.QueryParams{Type: "QueryByExample"})
require.Empty(t, results)
require.Error(t, err)
require.EqualError(t, err, toBeImplementedErr)
Expand Down
34 changes: 34 additions & 0 deletions pkg/doc/presexch/definition_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"context"
"crypto/sha256"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -1224,10 +1225,43 @@ func TestPresentationDefinition_CreateVP(t *testing.T) {
},
})

x := []*verifiable.Credential{
{
Context: []string{verifiable.ContextURI},
Types: []string{verifiable.VCType},
ID: uuid.New().String(),
Schemas: []verifiable.TypedID{{
ID: schemaURI,
Type: "JsonSchemaValidator2018",
}},
CustomFields: map[string]interface{}{
"first_name": "Jesse",
},
},
{
Context: []string{verifiable.ContextURI},
Types: []string{verifiable.VCType},
ID: uuid.New().String(),
Schemas: []verifiable.TypedID{{
ID: schemaURI,
}},
CustomFields: map[string]interface{}{
"first_name": "Jesse",
"last_name": "Travis",
},
},
}

b, _ := x[1].MarshalJSON()
fmt.Println("XXXXXssss>>>>", string(b))

require.NoError(t, err)
require.NotNil(t, vp)
require.Equal(t, 2, len(vp.Credentials()))

vpbts, _ := vp.MarshalJSON()
fmt.Println(">>", string(vpbts))

checkSubmission(t, vp, pd)
checkVP(t, vp)
})
Expand Down
21 changes: 19 additions & 2 deletions pkg/mock/storage/mock_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ type MockStore struct {
ErrPut error
ErrGet error
ErrDelete error
ErrQuery error
ErrNext error
ErrValue error
}

// Put stores the key and the record.
Expand Down Expand Up @@ -152,6 +155,10 @@ func (s *MockStore) GetBulk(keys ...string) ([][]byte, error) {
// If TagValue is not provided, then all data associated with the TagName will be returned.
// For now, expression can only be a single tag Name + Value pair.
func (s *MockStore) Query(expression string, _ ...storage.QueryOption) (storage.Iterator, error) {
if s.ErrQuery != nil {
return nil, s.ErrQuery
}

if expression == "" {
return nil, errInvalidQueryExpressionFormat
}
Expand All @@ -166,7 +173,7 @@ func (s *MockStore) Query(expression string, _ ...storage.QueryOption) (storage.

keys, dbEntries := s.getMatchingKeysAndDBEntries(expressionTagName, "")

return &iterator{keys: keys, dbEntries: dbEntries}, nil
return &iterator{keys: keys, dbEntries: dbEntries, errNext: s.ErrNext, errValue: s.ErrValue}, nil
case expressionTagNameAndValueLength:
expressionTagName := expressionSplit[0]
expressionTagValue := expressionSplit[1]
Expand All @@ -176,7 +183,7 @@ func (s *MockStore) Query(expression string, _ ...storage.QueryOption) (storage.

keys, dbEntries := s.getMatchingKeysAndDBEntries(expressionTagName, expressionTagValue)

return &iterator{keys: keys, dbEntries: dbEntries}, nil
return &iterator{keys: keys, dbEntries: dbEntries, errNext: s.ErrNext, errValue: s.ErrValue}, nil
default:
return nil, errInvalidQueryExpressionFormat
}
Expand Down Expand Up @@ -236,9 +243,15 @@ type iterator struct {
currentDBEntry DBEntry
keys []string
dbEntries []DBEntry
errNext error
errValue error
}

func (m *iterator) Next() (bool, error) {
if m.errNext != nil {
return false, m.errNext
}

if len(m.dbEntries) == m.currentIndex || len(m.dbEntries) == 0 {
m.dbEntries = nil
return false, nil
Expand All @@ -260,6 +273,10 @@ func (m *iterator) Key() (string, error) {
}

func (m *iterator) Value() ([]byte, error) {
if m.errValue != nil {
return nil, m.errValue
}

if len(m.dbEntries) == 0 {
return nil, errIteratorExhausted
}
Expand Down
33 changes: 32 additions & 1 deletion pkg/wallet/contents.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type ContentType string

const (
// Collection content type which can be used to group wallet contents together.
// https://w3c-ccg.github.io/universal-wallet-interop-spec/#Profile
// https://w3c-ccg.github.io/universal-wallet-interop-spec/#Collection
Collection ContentType = "collection"

// Credential content type for handling credential data models.
Expand Down Expand Up @@ -176,6 +176,37 @@ func (cs *contentStore) Get(ct ContentType, key string) ([]byte, error) {
return cs.store.Get(getContentKeyPrefix(ct, key))
}

// GetAll returns all wallet contents of give type.
// returns empty result when no data found.
func (cs *contentStore) GetAll(ct ContentType) ([]json.RawMessage, error) {
iter, err := cs.store.Query(ct.Name())
if err != nil {
return nil, err
}

var result []json.RawMessage

for {
ok, err := iter.Next()
if err != nil {
return nil, err
}

if !ok {
break
}

val, err := iter.Value()
if err != nil {
return nil, err
}

result = append(result, val)
}

return result, nil
}

func getContentID(content []byte) (string, error) {
var cid contentID
if err := json.Unmarshal(content, &cid); err != nil {
Expand Down
Loading

0 comments on commit ea43db4

Please sign in to comment.