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

schema and pkh refactor #224

Merged
merged 4 commits into from
Oct 12, 2022
Merged
Show file tree
Hide file tree
Changes from 2 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/did.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type DID interface {
// IsValid checks if the DID is compliant with its methods definition
IsValid() bool
// ToString Returns the string representation of the DID identifier (e.g. did:example:abcd)
ToString() string
String() string
// Suffix provides the value of the DID without the method prefix
Suffix() (string, error)
// Method provides the method for the DID
Expand Down
2 changes: 1 addition & 1 deletion did/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (d DIDKey) IsValid() bool {
return err == nil
}

func (d DIDKey) ToString() string {
func (d DIDKey) String() string {
return string(d)
}

Expand Down
2 changes: 1 addition & 1 deletion did/peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func (d DIDPeer) IsValid() bool {
return isPeerDID(string(d))
}

func (d DIDPeer) ToString() string {
func (d DIDPeer) String() string {
return string(d)
}

Expand Down
131 changes: 91 additions & 40 deletions did/pkh.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
package did

import (
"embed"
"fmt"
"regexp"
"strings"

"github.com/TBD54566975/ssi-sdk/cryptosuite"
"github.com/TBD54566975/ssi-sdk/schema"
"github.com/TBD54566975/ssi-sdk/util"
"github.com/goccy/go-json"
"github.com/pkg/errors"
)

Expand All @@ -17,6 +16,11 @@ type (
Network string
)

var (
//go:embed context
knownContexts embed.FS
)

const (
// DIDPKHPrefix did:pkh prefix
DIDPKHPrefix = "did:pkh"
Expand All @@ -29,24 +33,29 @@ const (
Polygon Network = "Polygon"
)

var didPKHNetworkPrefixMap = map[Network][]string{
Bitcoin: {"bip122:000000000019d6689c085ae165831e93", "EcdsaSecp256k1RecoveryMethod2020"},
Ethereum: {"eip155:1", "EcdsaSecp256k1RecoveryMethod2020"},
Polygon: {"eip155:137", "EcdsaSecp256k1RecoveryMethod2020"},
}
const (
BitcoinNetworkPrefix = "bip122:000000000019d6689c085ae165831e93"
EthereumNetworkPrefix = "eip155:1"
PolygonNetworkPrefix = "eip155:137"

// The following context should be manually inserted into each DID Document. This will likely change
// over time as new verification methods are supported, and general-purpose methods are specified.
var knownDIDPKHContext, _ = schema.GetKnownSchema(pkhContextFilename)
EcdsaSecp256k1RecoveryMethod2020 = "EcdsaSecp256k1RecoveryMethod2020"
)

// GetDIDPKHContext returns a context which should be manually inserted into each did:pkh document. This will likely
Copy link
Contributor

Choose a reason for hiding this comment

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

two spaces between context and which

// change over time as new verification methods are supported, and general-purpose methods are specified.
func GetDIDPKHContext() (string, error) {
b, err := knownContexts.ReadFile("context/" + pkhContextFilename)
return string(b), err
}

// CreateDIDPKHFromNetwork constructs a did:pkh from a network and the networks native address.
func CreateDIDPKHFromNetwork(network Network, address string) (*DIDPKH, error) {
if _, ok := didPKHNetworkPrefixMap[network]; ok {
split := strings.Split(didPKHNetworkPrefixMap[network][0], ":")
return CreateDIDPKH(split[0], split[1], address)
prefix, err := GetDIDPKHPrefixForNetwork(network)
if err != nil {
return nil, err
}

return nil, util.LoggingNewError(fmt.Sprintf("unsupported network: %s", string(network)))
split := strings.Split(prefix, ":")
return CreateDIDPKH(split[0], split[1], address)
}

// CreateDIDPKH constructs a did:pkh from a namespace, reference, and account address.
Expand All @@ -65,7 +74,7 @@ func (d DIDPKH) IsValid() bool {
return IsValidPKH(d)
}

func (d DIDPKH) ToString() string {
func (d DIDPKH) String() string {
return string(d)
}

Expand All @@ -82,14 +91,58 @@ func (d DIDPKH) Method() Method {
return PKHMethod
}

// GetNetwork returns the network by finding the network prefix in the did
func GetNetwork(did DIDPKH) (*Network, error) {
for network, prefix := range didPKHNetworkPrefixMap {
if strings.Contains(string(did), prefix[0]+":") {
return &network, nil
// GetDIDPKHPrefixForNetwork returns the did:pkh prefix for a given network
func GetDIDPKHPrefixForNetwork(n Network) (string, error) {
switch n {
case Bitcoin:
return BitcoinNetworkPrefix, nil
case Ethereum:
return EthereumNetworkPrefix, nil
case Polygon:
return PolygonNetworkPrefix, nil
}
return "", fmt.Errorf("unsupported did:pkh network: %s", n)
}

// GetDIDPKHNetworkForPrefix returns the did:pkh network for a given prefix
func GetDIDPKHNetworkForPrefix(p string) (Network, error) {
switch p {
case BitcoinNetworkPrefix:
return Bitcoin, nil
case EthereumNetworkPrefix:
return Ethereum, nil
case PolygonNetworkPrefix:
return Polygon, nil
}
return "", fmt.Errorf("unsupported did:pkh prefix: %s", p)
}

// GetDIDPKHNetworkForDID returns the network for a given did:pkh
func GetDIDPKHNetworkForDID(did string) (Network, error) {
prefixes := GetDIDPKHNetworkPrefixes()
for _, prefix := range prefixes {
if strings.Contains(did, prefix+":") {
return GetDIDPKHNetworkForPrefix(prefix)
}
}
return nil, util.LoggingNewError("network not supported")
return "", fmt.Errorf("could not find network for did:pkh DID: %s", did)
}

// GetVerificationTypeForNetwork returns the verification key type for a given network
func GetVerificationTypeForNetwork(n Network) (string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice, we don't have to have a mapping anymore, there could be a case where we add a new pkh but forget to add it here in the GetVerificationTypeForNetwork function, thats why I opt to have one source of truth mapping. But this works good too

Copy link
Member Author

Choose a reason for hiding this comment

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

good point, let me see how to define this once

switch n {
case Bitcoin, Ethereum, Polygon:
return EcdsaSecp256k1RecoveryMethod2020, nil
}
return "", fmt.Errorf("unsupported did:pkh network: %s", n)
}

func GetSupportedPKHNetworks() []Network {
return []Network{Bitcoin, Ethereum, Polygon}
}

func GetDIDPKHNetworkPrefixes() []string {
return []string{BitcoinNetworkPrefix, EthereumNetworkPrefix, PolygonNetworkPrefix}
}

// Expand turns the DID key into a complaint DID Document
Expand All @@ -100,17 +153,22 @@ func (d DIDPKH) Expand() (*DIDDocument, error) {
return nil, util.LoggingErrorMsg(err, "could not construct verification method")
}

var knownDIDPKHContextJSON interface{}
if err := json.Unmarshal([]byte(knownDIDPKHContext), &knownDIDPKHContextJSON); err != nil {
return nil, util.LoggingErrorMsg(err, "could not unmarshal known context json")
knownDIDPKHContextJSON, err := GetDIDPKHContext()
if err != nil {
return nil, util.LoggingErrorMsg(err, "could not get known context json")
}

contextJSON, err := util.ToJSONInterface(knownDIDPKHContextJSON)
if err != nil {
return nil, util.LoggingErrorMsg(err, "could not convert known context to json")
}

verificationMethodSet := []VerificationMethodSet{
string(d) + "#blockchainAccountId",
}

return &DIDDocument{
Context: knownDIDPKHContextJSON,
Context: contextJSON,
ID: string(d),
VerificationMethod: []VerificationMethod{*verificationMethod},
Authentication: verificationMethodSet,
Expand All @@ -128,21 +186,24 @@ func constructPKHVerificationMethod(did DIDPKH) (*VerificationMethod, error) {
}
}

network, err := GetNetwork(did)
network, err := GetDIDPKHNetworkForDID(did.String())
if err != nil {
return nil, util.LoggingErrorMsg(err, "could not find network")
}
verificationType := didPKHNetworkPrefixMap[*network][1]
verificationType, err := GetVerificationTypeForNetwork(network)
if err != nil {
return nil, util.LoggingErrorMsg(err, "could not find verification type")
}

parsed, err := did.Suffix()
suffix, err := did.Suffix()
if err != nil {
return nil, err
}
return &VerificationMethod{
ID: string(did) + "#blockchainAccountId",
Type: cryptosuite.LDKeyType(verificationType),
Controller: string(did),
BlockchainAccountID: parsed,
BlockchainAccountID: suffix,
}, nil
}

Expand Down Expand Up @@ -183,16 +244,6 @@ func IsValidPKH(did DIDPKH) bool {
return true
}

func GetSupportedNetworks() []Network {
var networks []Network

for network := range didPKHNetworkPrefixMap {
networks = append(networks, network)
}

return networks
}

type PKHResolver struct{}

func (r PKHResolver) Resolve(did string, opts ResolutionOptions) (*DIDResolutionResult, error) {
Expand Down
26 changes: 13 additions & 13 deletions did/pkh_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ var (
testVectorPKHDIDFS embed.FS
)

var PKHTestVectors = map[Network][]string{
Bitcoin: {"bip122:000000000019d6689c085ae165831e93", "did-pkh-bitcoin-doc.json"},
Ethereum: {"eip155:1", "did-pkh-ethereum-doc.json"},
Polygon: {"eip155:137", "did-pkh-polygon-doc.json"},
var pkhTestVectors = map[Network][]string{
Bitcoin: {BitcoinNetworkPrefix, "did-pkh-bitcoin-doc.json"},
Ethereum: {EthereumNetworkPrefix, "did-pkh-ethereum-doc.json"},
Polygon: {PolygonNetworkPrefix, "did-pkh-polygon-doc.json"},
}

func TestDIDPKHVectors(t *testing.T) {
// round trip serialize and de-serialize from json to our object model
for network, tv := range PKHTestVectors {
for network, tv := range pkhTestVectors {
gotTestVector, err := testVectorPKHDIDFS.ReadFile(testDataDirectory + "/" + tv[1])
assert.NoError(t, err)

Expand Down Expand Up @@ -96,7 +96,7 @@ func TestCreateDIDPKH(t *testing.T) {
generatedDIDDocBytes, err := json.Marshal(didDoc)
assert.NoError(tt, err)

testVectorDIDDoc, err := testVectorPKHDIDFS.ReadFile(testDataDirectory + "/" + PKHTestVectors[Ethereum][1])
testVectorDIDDoc, err := testVectorPKHDIDFS.ReadFile(testDataDirectory + "/" + pkhTestVectors[Ethereum][1])
assert.NoError(tt, err)

var expandedTestDIDDoc DIDDocument
Expand All @@ -111,7 +111,7 @@ func TestCreateDIDPKH(t *testing.T) {
t.Run("Test Unhappy Path", func(tt *testing.T) {
_, err := CreateDIDPKHFromNetwork("bad", "bad")
assert.Error(tt, err)
assert.Contains(tt, err.Error(), "unsupported network: bad")
assert.Contains(tt, err.Error(), "unsupported did:pkh network: bad")
})
}

Expand All @@ -138,34 +138,34 @@ func TestIsValidPKH(t *testing.T) {

func TestGetNetwork(t *testing.T) {
t.Run("Test Known Networks", func(tt *testing.T) {
for network := range PKHTestVectors {
for network := range pkhTestVectors {
didPKH, err := CreateDIDPKHFromNetwork(network, "dummyaddress")
assert.NoError(t, err)

n, err := GetNetwork(*didPKH)
n, err := GetDIDPKHNetworkForDID(didPKH.String())
assert.NoError(tt, err)

assert.Equal(tt, network, *n)
assert.Equal(tt, network, n)
}
})

// test bad network
t.Run("Test Unknown Network", func(tt *testing.T) {
_, err := CreateDIDPKHFromNetwork("bad", "dummyaddress")
assert.Error(tt, err)
assert.Contains(tt, err.Error(), "unsupported network: bad")
assert.Contains(tt, err.Error(), "unsupported did:pkh network: bad")
})
}

func TestGetSupportedNetworks(t *testing.T) {
supportedNetworks := GetSupportedNetworks()
supportedNetworks := GetSupportedPKHNetworks()

supportedNetworksSet := make(map[Network]bool)
for i := range supportedNetworks {
supportedNetworksSet[supportedNetworks[i]] = true
}

for network := range PKHTestVectors {
for network := range pkhTestVectors {
assert.True(t, supportedNetworksSet[network])
}
}
4 changes: 2 additions & 2 deletions did/resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func TestResolveDID(t *testing.T) {
_, didKey, err := GenerateDIDKey(crypto.Ed25519)
assert.NoError(t, err)
assert.NotEmpty(t, didKey)
doc, err := resolver.Resolve(didKey.ToString())
doc, err := resolver.Resolve(didKey.String())
assert.NoError(t, err)
assert.NotEmpty(t, doc)

Expand All @@ -33,7 +33,7 @@ func TestResolveDID(t *testing.T) {
didPKH, err := CreateDIDPKHFromNetwork(Ethereum, address)
assert.NoError(t, err)
assert.NotEmpty(t, didPKH)
doc, err = resolver.Resolve(didPKH.ToString())
doc, err = resolver.Resolve(didPKH.String())
assert.NoError(t, err)
assert.NotEmpty(t, doc)

Expand Down
6 changes: 3 additions & 3 deletions did/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ func (d DIDWeb) IsValid() bool {
return err == nil
}

func (d DIDWeb) ToString() string {
func (d DIDWeb) String() string {
return string(d)
}

func (d DIDWeb) Suffix() (string, error) {
split := strings.Split(d.ToString(), DIDWebPrefix+":")
split := strings.Split(d.String(), DIDWebPrefix+":")
if len(split) != 2 {
return "", errors.Wrap(util.InvalidFormatError, "did is malformed")
}
Expand Down Expand Up @@ -169,7 +169,7 @@ func (d DIDWeb) Resolve() (*DIDDocument, error) {
errMsg := fmt.Sprintf("could not resolve with docBytes %s", docBytes)
return nil, util.LoggingErrorMsg(err, errMsg)
}
if doc.ID != d.ToString() {
if doc.ID != d.String() {
errMsg := fmt.Sprintf("doc.ID %s does not match did:web value: %s", doc.ID, d)
return nil, util.LoggingNewError(errMsg)
}
Expand Down
Loading