diff --git a/dot/core/service.go b/dot/core/service.go index 4abd4cf53c4..da9825fe49a 100644 --- a/dot/core/service.go +++ b/dot/core/service.go @@ -498,6 +498,12 @@ func (s *Service) InsertKey(kp crypto.Keypair) { s.keys.Insert(kp) } +// HasKey returns true if given hex encoded public key string is found in keystore, false otherwise, error if there +// are issues decoding string +func (s *Service) HasKey(pubKeyStr string, keyType string) (bool, error) { + return keystore.HasKey(pubKeyStr, keyType, s.keys) +} + // GetRuntimeVersion gets the current RuntimeVersion func (s *Service) GetRuntimeVersion() (*runtime.VersionAPI, error) { //TODO ed, change this so that it can lookup runtime by block hash diff --git a/dot/core/service_test.go b/dot/core/service_test.go index 907164cc5a3..ad46717cf3d 100644 --- a/dot/core/service_test.go +++ b/dot/core/service_test.go @@ -122,7 +122,8 @@ func TestStartService(t *testing.T) { err := s.Start() require.Nil(t, err) - s.Stop() + err = s.Stop() + require.NoError(t, err) } func TestNotAuthority(t *testing.T) { @@ -209,3 +210,35 @@ func TestCheckForRuntimeChanges(t *testing.T) { err = s.checkForRuntimeChanges() require.Nil(t, err) } + +func TestService_HasKey(t *testing.T) { + ks := keystore.NewKeystore() + kr, err := keystore.NewSr25519Keyring() + require.NoError(t, err) + ks.Insert(kr.Alice) + + cfg := &Config{ + Keystore: ks, + } + svc := NewTestService(t, cfg) + + res, err := svc.HasKey(kr.Alice.Public().Hex(), "babe") + require.NoError(t, err) + require.True(t, res) +} + +func TestService_HasKey_UnknownType(t *testing.T) { + ks := keystore.NewKeystore() + kr, err := keystore.NewSr25519Keyring() + require.NoError(t, err) + ks.Insert(kr.Alice) + + cfg := &Config{ + Keystore: ks, + } + svc := NewTestService(t, cfg) + + res, err := svc.HasKey(kr.Alice.Public().Hex(), "xxxx") + require.EqualError(t, err, "unknown key type: xxxx") + require.False(t, res) +} diff --git a/dot/rpc/modules/api.go b/dot/rpc/modules/api.go index fb116453bb4..0459200afea 100644 --- a/dot/rpc/modules/api.go +++ b/dot/rpc/modules/api.go @@ -43,6 +43,7 @@ type TransactionQueueAPI interface { // CoreAPI is the interface for the core methods type CoreAPI interface { InsertKey(kp crypto.Keypair) + HasKey(pubKeyStr string, keyType string) (bool, error) GetRuntimeVersion() (*runtime.VersionAPI, error) IsBabeAuthority() bool HandleSubmittedExtrinsic(types.Extrinsic) error diff --git a/dot/rpc/modules/author.go b/dot/rpc/modules/author.go index 561da2851b1..9a6bcfa582d 100644 --- a/dot/rpc/modules/author.go +++ b/dot/rpc/modules/author.go @@ -21,13 +21,10 @@ import ( "net/http" "reflect" - "github.com/ChainSafe/gossamer/lib/crypto" - "github.com/ChainSafe/gossamer/lib/keystore" - "github.com/ChainSafe/gossamer/dot/types" "github.com/ChainSafe/gossamer/lib/common" + "github.com/ChainSafe/gossamer/lib/keystore" "github.com/ChainSafe/gossamer/lib/transaction" - log "github.com/ChainSafe/log15" ) @@ -95,12 +92,12 @@ func NewAuthorModule(coreAPI CoreAPI, runtimeAPI RuntimeAPI, txQueueAPI Transact func (cm *AuthorModule) InsertKey(r *http.Request, req *KeyInsertRequest, res *KeyInsertResponse) error { keyReq := *req - pkDec, err := common.HexToHash(keyReq[1]) + pkDec, err := common.HexToBytes(keyReq[1]) if err != nil { return err } - privateKey, err := keystore.DecodePrivateKey(pkDec.ToBytes(), determineKeyType(keyReq[0])) + privateKey, err := keystore.DecodePrivateKey(pkDec, keystore.DetermineKeyType(keyReq[0])) if err != nil { return err } @@ -119,6 +116,14 @@ func (cm *AuthorModule) InsertKey(r *http.Request, req *KeyInsertRequest, res *K return nil } +// HasKey Checks if the keystore has private keys for the given public key and key type. +func (cm *AuthorModule) HasKey(r *http.Request, req *[]string, res *bool) error { + reqKey := *req + var err error + *res, err = cm.coreAPI.HasKey(reqKey[0], reqKey[1]) + return err +} + // PendingExtrinsics Returns all pending extrinsics func (cm *AuthorModule) PendingExtrinsics(r *http.Request, req *EmptyRequest, res *PendingExtrinsicsResponse) error { pending := cm.txQueueAPI.Pending() @@ -188,26 +193,3 @@ func (cm *AuthorModule) SubmitExtrinsic(r *http.Request, req *Extrinsic, res *Ex return err } - -// determineKeyType takes string as defined in https://github.com/w3f/PSPs/blob/psp-rpc-api/psp-002.md#Key-types -// and returns the crypto.KeyType -func determineKeyType(t string) crypto.KeyType { - // TODO: create separate keystores for different key types, issue #768 - switch t { - case "babe": - return crypto.Sr25519Type - case "gran": - return crypto.Sr25519Type - case "acco": - return crypto.Sr25519Type - case "aura": - return crypto.Sr25519Type - case "imon": - return crypto.Sr25519Type - case "audi": - return crypto.Sr25519Type - case "dumy": - return crypto.Sr25519Type - } - return "unknown keytype" -} diff --git a/dot/rpc/modules/author_test.go b/dot/rpc/modules/author_test.go index 8f65c07f55a..3d49221a284 100644 --- a/dot/rpc/modules/author_test.go +++ b/dot/rpc/modules/author_test.go @@ -43,7 +43,8 @@ func TestAuthorModule_Pending(t *testing.T) { Validity: new(transaction.Validity), } - txQueue.Push(vtx) + _, err = txQueue.Push(vtx) + require.NoError(t, err) err = auth.PendingExtrinsics(nil, nil, res) if err != nil { @@ -174,7 +175,7 @@ func TestAuthorModule_InsertKey_Valid_gran_keytype(t *testing.T) { cs := core.NewTestService(t, nil) auth := NewAuthorModule(cs, nil, nil) - req := &KeyInsertRequest{"gran", "0xb7e9185065667390d2ad952a5324e8c365c9bf503dcf97c67a5ce861afe97309", "0x6246ddf254e0b4b4e7dffefc8adf69d212b98ac2b579c362b473fec8c40b4c0a"} + req := &KeyInsertRequest{"gran", "0xb7e9185065667390d2ad952a5324e8c365c9bf503dcf97c67a5ce861afe97309b7e9185065667390d2ad952a5324e8c365c9bf503dcf97c67a5ce861afe97309", "0xb7e9185065667390d2ad952a5324e8c365c9bf503dcf97c67a5ce861afe97309"} res := &KeyInsertResponse{} err := auth.InsertKey(nil, req, res) require.Nil(t, err) @@ -203,6 +204,52 @@ func TestAuthorModule_InsertKey_UnknownKeyType(t *testing.T) { } +func TestAuthorModule_HasKey(t *testing.T) { + auth := setupAuthModule(t, nil) + kr, err := keystore.NewSr25519Keyring() + require.Nil(t, err) + + var res bool + req := []string{kr.Alice.Public().Hex(), "babe"} + err = auth.HasKey(nil, &req, &res) + require.NoError(t, err) + require.True(t, res) +} + +func TestAuthorModule_HasKey_NotFound(t *testing.T) { + auth := setupAuthModule(t, nil) + kr, err := keystore.NewSr25519Keyring() + require.Nil(t, err) + + var res bool + req := []string{kr.Bob.Public().Hex(), "babe"} + err = auth.HasKey(nil, &req, &res) + require.NoError(t, err) + require.False(t, res) +} + +func TestAuthorModule_HasKey_InvalidKey(t *testing.T) { + auth := setupAuthModule(t, nil) + + var res bool + req := []string{"0xaa11", "babe"} + err := auth.HasKey(nil, &req, &res) + require.EqualError(t, err, "cannot create public key: input is not 32 bytes") + require.False(t, res) +} + +func TestAuthorModule_HasKey_InvalidKeyType(t *testing.T) { + auth := setupAuthModule(t, nil) + kr, err := keystore.NewSr25519Keyring() + require.Nil(t, err) + + var res bool + req := []string{kr.Alice.Public().Hex(), "xxxx"} + err = auth.HasKey(nil, &req, &res) + require.EqualError(t, err, "unknown key type: xxxx") + require.False(t, res) +} + func newCoreService(t *testing.T) *core.Service { // setup service tt := trie.NewEmptyTrie() @@ -220,6 +267,11 @@ func newCoreService(t *testing.T) *core.Service { ks := keystore.NewKeystore() ks.Insert(kp) + // insert alice key for testing + kr, err := keystore.NewSr25519Keyring() + require.NoError(t, err) + ks.Insert(kr.Alice) + cfg := &core.Config{ Runtime: rt, Keystore: ks, diff --git a/dot/rpc/service_test.go b/dot/rpc/service_test.go index 987a7557172..0e9a192b42c 100644 --- a/dot/rpc/service_test.go +++ b/dot/rpc/service_test.go @@ -29,7 +29,7 @@ func TestNewService(t *testing.T) { func TestService_Methods(t *testing.T) { qtySystemMethods := 7 qtyRPCMethods := 1 - qtyAuthorMethods := 6 + qtyAuthorMethods := 7 rpcService := NewService() sysMod := modules.NewSystemModule(nil, nil) diff --git a/lib/keystore/helpers.go b/lib/keystore/helpers.go index a3c681b935f..51a52450b8e 100644 --- a/lib/keystore/helpers.go +++ b/lib/keystore/helpers.go @@ -252,3 +252,53 @@ func UnlockKeys(ks *Keystore, dir string, unlock string, password string) error return nil } + +// DetermineKeyType takes string as defined in https://github.com/w3f/PSPs/blob/psp-rpc-api/psp-002.md#Key-types +// and returns the crypto.KeyType +func DetermineKeyType(t string) crypto.KeyType { + // TODO: create separate keystores for different key types, issue #768 + switch t { + case "babe": + return crypto.Sr25519Type + case "gran": + return crypto.Ed25519Type + case "acco": + return crypto.Sr25519Type + case "aura": + return crypto.Sr25519Type + case "imon": + return crypto.Sr25519Type + case "audi": + return crypto.Sr25519Type + case "dumy": + return crypto.Sr25519Type + } + return "unknown keytype" +} + +// HasKey returns true if given hex encoded public key string is found in keystore, false otherwise, error if there +// are issues decoding string +func HasKey(pubKeyStr string, keyType string, keystore *Keystore) (bool, error) { + keyBytes, err := common.HexToBytes(pubKeyStr) + if err != nil { + return false, err + } + cKeyType := DetermineKeyType(keyType) + + var pubKey crypto.PublicKey + // TODO: consider handling for different key types, see issue #768 + switch cKeyType { + case crypto.Sr25519Type: + pubKey, err = sr25519.NewPublicKey(keyBytes) + case crypto.Ed25519Type: + pubKey, err = ed25519.NewPublicKey(keyBytes) + default: + err = fmt.Errorf("unknown key type: %s", keyType) + } + + if err != nil { + return false, err + } + key := keystore.Get(pubKey.Address()) + return key != nil, nil +} diff --git a/lib/keystore/helpers_test.go b/lib/keystore/helpers_test.go index f0eb8b09051..a80d29875ae 100644 --- a/lib/keystore/helpers_test.go +++ b/lib/keystore/helpers_test.go @@ -19,6 +19,7 @@ package keystore import ( "testing" + "github.com/ChainSafe/gossamer/lib/crypto" "github.com/stretchr/testify/require" ) @@ -28,3 +29,24 @@ func TestLoadKeystore(t *testing.T) { require.Equal(t, 1, ks.NumSr25519Keys()) } + +var testKeyTypes = []struct { + testType string + expectedType string +}{ + {testType: "babe", expectedType: crypto.Sr25519Type}, + {testType: "gran", expectedType: crypto.Ed25519Type}, + {testType: "acco", expectedType: crypto.Sr25519Type}, + {testType: "aura", expectedType: crypto.Sr25519Type}, + {testType: "imon", expectedType: crypto.Sr25519Type}, + {testType: "audi", expectedType: crypto.Sr25519Type}, + {testType: "dumy", expectedType: crypto.Sr25519Type}, + {testType: "xxxx", expectedType: "unknown keytype"}, +} + +func TestDetermineKeyType(t *testing.T) { + for _, test := range testKeyTypes { + output := DetermineKeyType(test.testType) + require.Equal(t, test.expectedType, output) + } +}