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 web5 spec resolve test vectors for did dht #125

Merged
merged 7 commits into from
Mar 29, 2024
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
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# Fetches all history for all tags and branches
fetch-depth: 0
# Initializes submodules recursively
submodules: recursive
- uses: ./.github/actions/setup
- run: just test
lint:
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "web5-spec"]
path = web5-spec
url = [email protected]:TBD54566975/web5-spec.git
mihai-chiorean marked this conversation as resolved.
Show resolved Hide resolved
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@ JWT signing and verification using DIDs
# Development

## Prerequisites
We use a submodule for test vectors that make sure we follow the appropriate spec. Running tests will fail without it.
To set up the submodule, clone using:

```
git clone --recurse-submodules [email protected]:TBD54566975/web5-go.git
```

If you've already cloned, add submodules:

```
git submodule update --init
```

### [`hermit`](https://cashapp.github.io/hermit/)
This repo uses hermit to manage all environment dependencies (e.g. `just`, `go`).
Expand Down
8 changes: 4 additions & 4 deletions dids/diddht/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,27 +48,27 @@ func (r *Resolver) ResolveWithContext(ctx context.Context, uri string) (didcore.
}

if did.Method != "dht" {
return didcore.ResolutionResultWithError("invalidDid"), didcore.ResolutionError{Code: "invalidDid"}
return didcore.ResolutionResultWithError("methodNotSupported"), didcore.ResolutionError{Code: "methodNotSupported"}
}

// 2. ensure did ID is zbase32
identifier, err := zbase32.DecodeString(did.ID)
if err != nil {
// TODO log err
return didcore.ResolutionResultWithError("invalidDid"), didcore.ResolutionError{Code: "invalidDid"}
return didcore.ResolutionResultWithError("invalidPublicKey"), didcore.ResolutionError{Code: "invalidPublicKey"}
}

if len(identifier) == 0 {
// return nil, fmt.Errorf("no bytes decoded from zbase32 identifier %s", did.ID)
// TODO log err
return didcore.ResolutionResultWithError("invalidDid"), didcore.ResolutionError{Code: "invalidDid"}
return didcore.ResolutionResultWithError("invalidPublicKey"), didcore.ResolutionError{Code: "invalidPublicKey"}
}

// 3. fetch from the relay
bep44Message, err := r.relay.FetchWithContext(ctx, did.ID)
if err != nil {
// TODO log err
return didcore.ResolutionResultWithError("invalidDid"), didcore.ResolutionError{Code: "invalidDid"}
return didcore.ResolutionResultWithError("notFound"), didcore.ResolutionError{Code: "notFound"}
}

// get the dns payload from the bep44 message
Expand Down
119 changes: 119 additions & 0 deletions dids/diddht/resolver_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package diddht

import (
"encoding/hex"
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"testing"

"github.com/alecthomas/assert/v2"
)

const dhtSpecVectors string = "../../web5-spec/test-vectors/did_dht/resolve.json"

type vector struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

we'll need to move some of this to a shared space in the code repo because we'll need a common interface to retrieve the test vector for the given test case, but we could do that in a follow-up when we expand the scope of test vectors in this repo

Copy link
Contributor Author

Choose a reason for hiding this comment

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

agreed. I want to add "create" vectors too. I could make that change there.

Description string `json:"description"`
Input struct {
DIDUri string `json:"didUri"`
} `json:"input"`
Output struct {
DIDResolutionMetadata struct {
Error string `json:"error"`
} `json:"didResolutionMetadata"`
} `json:"output"`
Errors bool `json:"errors"`
}

func initVector() ([]vector, error) {
// Load test vectors from file
data, err := os.ReadFile(dhtSpecVectors)
if err != nil {
return nil, err
}

// Unmarshal test vectors
vectorData := struct {
Vectors []vector `json:"vectors"`
}{}
if err := json.Unmarshal(data, &vectorData); err != nil {
return nil, err
}
return vectorData.Vectors, nil
}

func Test_VectorsResolve(t *testing.T) {
vectors, err := initVector()
assert.NoError(t, err)

mocks := map[string]vector{}
for _, v := range vectors {
mocks[v.Input.DIDUri] = v
}

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
return
}))
defer ts.Close()

r := NewResolver(ts.URL, http.DefaultClient)

for _, v := range vectors {
vector := v
t.Run(vector.Description, func(t *testing.T) {
res, err := r.Resolve(vector.Input.DIDUri)
if vector.Errors {
assert.True(t, err != nil)
assert.Equal(t, res.ResolutionMetadata.Error, vector.Output.DIDResolutionMetadata.Error)
}
})
}
}

func Test_resolve(t *testing.T) {

// vector taken from https://github.com/TBD54566975/web5-js/blob/91d52aaa9410db5e5f7c3c31ebfe0d4956028496/packages/dids/tests/methods/did-dht.spec.ts#L725
vectors := map[string]string{
"did:dht:9tjoow45ef1hksoo96bmzkwwy3mhme95d7fsi3ezjyjghmp75qyo": "ea33e704f3a48a3392f54b28744cdfb4e24780699f92ba7df62fd486d2a2cda3f263e1c6bcbd" +
"75d438be7316e5d6e94b13e98151f599cfecefad0b37432bd90a0000000065b0ed1600008400" +
"0000000300000000035f6b30045f6469643439746a6f6f773435656631686b736f6f3936626d" +
"7a6b777779336d686d653935643766736933657a6a796a67686d70373571796f000010000100" +
"001c2000373669643d303b743d303b6b3d5f464d49553174425a63566145502d437536715542" +
"6c66466f5f73665332726c4630675362693239323445045f747970045f6469643439746a6f6f" +
"773435656631686b736f6f3936626d7a6b777779336d686d653935643766736933657a6a796a" +
"67686d70373571796f000010000100001c2000070669643d372c36045f6469643439746a6f6f" +
"773435656631686b736f6f3936626d7a6b777779336d686d653935643766736933657a6a796a" +
"67686d70373571796f000010000100001c20002726763d303b766d3d6b303b617574683d6b30" +
"3b61736d3d6b303b64656c3d6b303b696e763d6b30",
}

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
did := "did:dht:" + r.URL.Path[1:]
defer r.Body.Close()
buf, ok := vectors[did]
if !ok {
w.WriteHeader(http.StatusNotFound)
return
}
data, err := hex.DecodeString(buf)
assert.NoError(t, err)
_, err = w.Write(data)
assert.NoError(t, err)

}))
defer ts.Close()

r := NewResolver(ts.URL, http.DefaultClient)

for k := range vectors {
did := k
t.Run(did, func(t *testing.T) {
res, err := r.Resolve(did)
assert.NoError(t, err)
assert.NotZero(t, res.Document)
assert.Equal(t, res.Document.ID, did)
})
}
}
1 change: 1 addition & 0 deletions web5-spec
Submodule web5-spec added at 77f2cb
Loading