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

dot/rpc: Implement RPC method author_hasKey #877

Merged
merged 9 commits into from
May 25, 2020
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) {
Copy link
Contributor

Choose a reason for hiding this comment

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

this would probably be better as a function in the keystore package that can be wrapped by core

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 idea, updated.

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)
}
}