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 author_insertKey #765

Merged
merged 8 commits into from
Apr 10, 2020
Merged
Show file tree
Hide file tree
Changes from 6 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
8 changes: 8 additions & 0 deletions dot/core/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ import (
"math/big"
"sync"

"github.com/ChainSafe/gossamer/lib/crypto"

"github.com/ChainSafe/gossamer/dot/core/types"
"github.com/ChainSafe/gossamer/dot/network"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/babe"
Expand Down Expand Up @@ -500,3 +503,8 @@ func (s *Service) checkForRuntimeChanges() error {

return nil
}

// InsertKey inserts keypair into keystore
func (s *Service) InsertKey(kp crypto.Keypair) {
s.keys.Insert(kp)
}
8 changes: 6 additions & 2 deletions dot/rpc/modules/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package modules
import (
"math/big"

"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/crypto"

"github.com/ChainSafe/gossamer/dot/core/types"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/transaction"
)
Expand Down Expand Up @@ -35,4 +37,6 @@ type TransactionQueueAPI interface {
}

// CoreAPI is the interface for the core methods
type CoreAPI interface{}
type CoreAPI interface {
InsertKey(kp crypto.Keypair)
}
59 changes: 52 additions & 7 deletions dot/rpc/modules/author.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@
package modules

import (
"fmt"
"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"
Expand All @@ -33,11 +38,7 @@ type AuthorModule struct {
}

// KeyInsertRequest is used as model for the JSON
type KeyInsertRequest struct {
KeyType string `json:"keyType"`
Suri string `json:"suri"`
PublicKey []byte `json:"publicKey"`
}
type KeyInsertRequest []string

// Extrinsic represents a hex-encoded extrinsic
type Extrinsic string
Expand All @@ -52,7 +53,6 @@ type ExtrinsicOrHash struct {
type ExtrinsicOrHashRequest []ExtrinsicOrHash

// KeyInsertResponse []byte
// TODO: Waiting on Block type defined here https://github.com/ChainSafe/gossamer/pull/233
type KeyInsertResponse []byte

// PendingExtrinsicsResponse is a bi-dimensional array of bytes for allocating the pending extrisics
Expand Down Expand Up @@ -91,7 +91,29 @@ func NewAuthorModule(coreAPI CoreAPI, txQueueAPI TransactionQueueAPI) *AuthorMod

// InsertKey inserts a key into the keystore
func (cm *AuthorModule) InsertKey(r *http.Request, req *KeyInsertRequest, res *KeyInsertResponse) error {
_ = cm.coreAPI
keyReq := *req

pkDec, err := common.HexToHash(keyReq[1])
if err != nil {
return err
}

privateKey, err := keystore.DecodePrivateKey(pkDec.ToBytes(), determineKeyType(keyReq[0]))
if err != nil {
return err
}

keyPair, err := keystore.PrivateKeyToKeypair(privateKey)
if err != nil {
return err
}

if !reflect.DeepEqual(keyPair.Public().Hex(), keyReq[2]) {
return fmt.Errorf("generated public key does not equal provide public key")
}

cm.coreAPI.InsertKey(keyPair)
log.Info("[rpc] inserted key into keystore", "key", keyPair.Public().Hex())
return nil
}

Expand Down Expand Up @@ -155,3 +177,26 @@ func (cm *AuthorModule) SubmitExtrinsic(r *http.Request, req *Extrinsic, res *Ex
log.Info("[rpc] submitted extrinsic", "tx", vtx, "hash", hash.String())
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.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"
}
104 changes: 104 additions & 0 deletions dot/rpc/modules/author_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,87 @@ package modules

import (
"fmt"
"math/big"
"reflect"
"testing"

"github.com/ChainSafe/gossamer/dot/core"
"github.com/ChainSafe/gossamer/dot/network"
"github.com/ChainSafe/gossamer/lib/genesis"
"github.com/ChainSafe/gossamer/lib/keystore"
"github.com/ChainSafe/gossamer/lib/runtime"
"github.com/ChainSafe/gossamer/lib/trie"
"github.com/stretchr/testify/require"

"github.com/ChainSafe/gossamer/dot/core/types"
"github.com/ChainSafe/gossamer/dot/state"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/transaction"
)

var testExt = []byte{3, 16, 110, 111, 111, 116, 1, 64, 103, 111, 115, 115, 97, 109, 101, 114, 95, 105, 115, 95, 99, 111, 111, 108}

// testGenesisHeader is a test block header
var testGenesisHeader = &types.Header{
Number: big.NewInt(0),
StateRoot: trie.EmptyHash,
}

// newTestService creates a new test core service
func newTestService(t *testing.T, cfg *core.Config) *core.Service {
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe we could move this to dot/core/test_helpers.go and export it? just so we don't need to duplicate code

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!

if cfg == nil {
rt := runtime.NewTestRuntime(t, runtime.POLKADOT_RUNTIME_c768a7e4c70e)
cfg = &core.Config{
Runtime: rt,
IsBabeAuthority: false,
}
}

if cfg.Keystore == nil {
cfg.Keystore = keystore.NewKeystore()
}

if cfg.NewBlocks == nil {
cfg.NewBlocks = make(chan types.Block)
}

if cfg.MsgRec == nil {
cfg.MsgRec = make(chan network.Message, 10)
}

if cfg.MsgSend == nil {
cfg.MsgSend = make(chan network.Message, 10)
}

if cfg.SyncChan == nil {
cfg.SyncChan = make(chan *big.Int, 10)
}

stateSrvc := state.NewService("")
stateSrvc.UseMemDB()

genesisData := new(genesis.Data)

err := stateSrvc.Initialize(genesisData, testGenesisHeader, trie.NewEmptyTrie())
require.Nil(t, err)

err = stateSrvc.Start()
require.Nil(t, err)

if cfg.BlockState == nil {
cfg.BlockState = stateSrvc.Block
}

if cfg.StorageState == nil {
cfg.StorageState = stateSrvc.Storage
}

s, err := core.NewService(cfg)
require.Nil(t, err)

return s
}

func TestAuthorModule_Pending(t *testing.T) {
txQueue := state.NewTransactionQueue()
auth := NewAuthorModule(nil, txQueue)
Expand Down Expand Up @@ -70,3 +141,36 @@ func TestAuthorModule_SubmitExtrinsic(t *testing.T) {
t.Fatalf("Fail: got %v expected %v", inQueue, expected)
}
}

func TestAuthorModule_InsertKey_Valid(t *testing.T) {
cs := newTestService(t, nil)

auth := NewAuthorModule(cs, nil)
req := &KeyInsertRequest{"babe", "0xb7e9185065667390d2ad952a5324e8c365c9bf503dcf97c67a5ce861afe97309", "0x6246ddf254e0b4b4e7dffefc8adf69d212b98ac2b579c362b473fec8c40b4c0a"}
res := &KeyInsertResponse{}
err := auth.InsertKey(nil, req, res)
require.Nil(t, err)

require.Len(t, *res, 0) // zero len result on success
}

func TestAuthorModule_InsertKey_InValid(t *testing.T) {
cs := newTestService(t, nil)

auth := NewAuthorModule(cs, nil)
req := &KeyInsertRequest{"babe", "0xb7e9185065667390d2ad952a5324e8c365c9bf503dcf97c67a5ce861afe97309", "0x0000000000000000000000000000000000000000000000000000000000000000"}
res := &KeyInsertResponse{}
err := auth.InsertKey(nil, req, res)
require.EqualError(t, err, "generated public key does not equal provide public key")
}

func TestAuthorModule_InsertKey_UnknownKeyType(t *testing.T) {
cs := newTestService(t, nil)

auth := NewAuthorModule(cs, nil)
req := &KeyInsertRequest{"mack", "0xb7e9185065667390d2ad952a5324e8c365c9bf503dcf97c67a5ce861afe97309", "0x6246ddf254e0b4b4e7dffefc8adf69d212b98ac2b579c362b473fec8c40b4c0a"}
res := &KeyInsertResponse{}
err := auth.InsertKey(nil, req, res)
require.EqualError(t, err, "cannot decode key: invalid key type")

}