Skip to content

Commit

Permalink
dot/rpc: Implement RPC method author_hasKey (#877)
Browse files Browse the repository at this point in the history
* add stub for rpc call author_hasKey

* implement rpc author_hasKey

* add tests

* update service test

* add tests

* Move HasKey function to keystore package.

Updated key handling for grandpa to use Ed25519 type keys.
  • Loading branch information
edwardmack authored and ryanchristo committed Jun 24, 2020
1 parent 9501d86 commit 788023b
Show file tree
Hide file tree
Showing 8 changed files with 179 additions and 33 deletions.
6 changes: 6 additions & 0 deletions dot/core/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
35 changes: 34 additions & 1 deletion dot/core/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
}
1 change: 1 addition & 0 deletions dot/rpc/modules/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
40 changes: 11 additions & 29 deletions dot/rpc/modules/author.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand Down Expand Up @@ -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
}
Expand All @@ -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()
Expand Down Expand Up @@ -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"
}
56 changes: 54 additions & 2 deletions dot/rpc/modules/author_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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()
Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion dot/rpc/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
50 changes: 50 additions & 0 deletions lib/keystore/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
22 changes: 22 additions & 0 deletions lib/keystore/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package keystore
import (
"testing"

"github.com/ChainSafe/gossamer/lib/crypto"
"github.com/stretchr/testify/require"
)

Expand All @@ -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)
}
}

0 comments on commit 788023b

Please sign in to comment.