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 system_accountNextIndex #1376

Merged
merged 16 commits into from
Feb 17, 2021
Merged
Show file tree
Hide file tree
Changes from 5 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
3 changes: 2 additions & 1 deletion dot/rpc/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ func (h *HTTPServer) RegisterModules(mods []string) {
var srvc interface{}
switch mod {
case "system":
srvc = modules.NewSystemModule(h.serverConfig.NetworkAPI, h.serverConfig.SystemAPI)
srvc = modules.NewSystemModule(h.serverConfig.NetworkAPI, h.serverConfig.SystemAPI,
h.serverConfig.CoreAPI, h.serverConfig.StorageAPI)
case "author":
srvc = modules.NewAuthorModule(h.logger, h.serverConfig.CoreAPI, h.serverConfig.RuntimeAPI, h.serverConfig.TransactionQueueAPI)
case "chain":
Expand Down
62 changes: 61 additions & 1 deletion dot/rpc/modules/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,21 @@
package modules

import (
"errors"
"net/http"

"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/crypto"
"github.com/ChainSafe/gossamer/lib/scale"
"github.com/centrifuge/go-substrate-rpc-client/v2/types"
)

// SystemModule is an RPC module providing access to core API points
type SystemModule struct {
networkAPI NetworkAPI
systemAPI SystemAPI
coreAPI CoreAPI
storageAPI StorageAPI
}

// EmptyRequest represents an RPC request with no fields
Expand All @@ -51,11 +57,21 @@ type SystemNetworkStateResponse struct {
// SystemPeersResponse struct to marshal json
type SystemPeersResponse []common.PeerInfo

// U64Response holds U64 response
type U64Response uint64

// StringRequest holds string request
type StringRequest struct {
String string
}

// NewSystemModule creates a new API instance
func NewSystemModule(net NetworkAPI, sys SystemAPI) *SystemModule {
func NewSystemModule(net NetworkAPI, sys SystemAPI, core CoreAPI, storage StorageAPI) *SystemModule {
return &SystemModule{
networkAPI: net, // TODO: migrate to network state
systemAPI: sys,
coreAPI: core,
storageAPI: storage,
}
}

Expand Down Expand Up @@ -135,3 +151,47 @@ func (sm *SystemModule) NodeRoles(r *http.Request, req *EmptyRequest, res *[]int
*res = resultArray
return nil
}

// AccountNextIndex Returns the next valid index (aka. nonce) for given account.
func (sm *SystemModule) AccountNextIndex(r *http.Request, req *StringRequest, res *U64Response) error {
// todo (ed) check pending transactions

// get metadata to build storage storageKey
rawMeta, err := sm.coreAPI.GetMetadata(nil)
if err != nil {
return err
}
sdMeta, err := scale.Decode(rawMeta, []byte{})
if err != nil {
return err
}
var metadata types.Metadata
err = types.DecodeFromBytes(sdMeta.([]byte), &metadata)
if err != nil {
return err
}

if req == nil || len(req.String) == 0 {
return errors.New("Account address must be valid")
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe move this to the top of the function so it checks it first?

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.

}
addressPubKey := crypto.PublicAddressToByteArray(common.Address(req.String))

storageKey, err := types.CreateStorageKey(&metadata, "System", "Account", addressPubKey, nil)
if err != nil {
return err
}

accountRaw, err := sm.storageAPI.GetStorage(nil, storageKey)
if err != nil {
return err
}

var accountInfo types.AccountInfo
err = types.DecodeFromBytes(accountRaw, &accountInfo)
if err != nil {
return err
}

*res = U64Response(accountInfo.Nonce)
return nil
}
66 changes: 57 additions & 9 deletions dot/rpc/modules/system_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/ChainSafe/gossamer/dot/network"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/scale"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -96,7 +97,7 @@ func newNetworkService(t *testing.T) *network.Service {
// Test RPC's System.Health() response
func TestSystemModule_Health(t *testing.T) {
net := newNetworkService(t)
sys := NewSystemModule(net, nil)
sys := NewSystemModule(net, nil, nil, nil)

res := &SystemHealthResponse{}
err := sys.Health(nil, nil, res)
Expand All @@ -110,7 +111,7 @@ func TestSystemModule_Health(t *testing.T) {
// Test RPC's System.NetworkState() response
func TestSystemModule_NetworkState(t *testing.T) {
net := newNetworkService(t)
sys := NewSystemModule(net, nil)
sys := NewSystemModule(net, nil, nil, nil)

res := &SystemNetworkStateResponse{}
err := sys.NetworkState(nil, nil, res)
Expand All @@ -126,7 +127,7 @@ func TestSystemModule_NetworkState(t *testing.T) {
// Test RPC's System.Peers() response
func TestSystemModule_Peers(t *testing.T) {
net := newNetworkService(t)
sys := NewSystemModule(net, nil)
sys := NewSystemModule(net, nil, nil, nil)

res := &SystemPeersResponse{}
err := sys.Peers(nil, nil, res)
Expand All @@ -139,7 +140,7 @@ func TestSystemModule_Peers(t *testing.T) {

func TestSystemModule_NodeRoles(t *testing.T) {
net := newNetworkService(t)
sys := NewSystemModule(net, nil)
sys := NewSystemModule(net, nil, nil, nil)
expected := []interface{}{"Full"}

var res []interface{}
Expand Down Expand Up @@ -187,7 +188,7 @@ func (api *mockSystemAPI) ChainType() string {
}

func TestSystemModule_Chain(t *testing.T) {
sys := NewSystemModule(nil, newMockSystemAPI())
sys := NewSystemModule(nil, newMockSystemAPI(), nil, nil)

res := new(string)
err := sys.Chain(nil, nil, res)
Expand All @@ -198,15 +199,15 @@ func TestSystemModule_Chain(t *testing.T) {
func TestSystemModule_ChainType(t *testing.T) {
api := newMockSystemAPI()

sys := NewSystemModule(nil, api)
sys := NewSystemModule(nil, api, nil, nil)

res := new(string)
sys.ChainType(nil, nil, res)
require.Equal(t, api.info.ChainType, *res)
}

func TestSystemModule_Name(t *testing.T) {
sys := NewSystemModule(nil, newMockSystemAPI())
sys := NewSystemModule(nil, newMockSystemAPI(), nil, nil)

res := new(string)
err := sys.Name(nil, nil, res)
Expand All @@ -215,7 +216,7 @@ func TestSystemModule_Name(t *testing.T) {
}

func TestSystemModule_Version(t *testing.T) {
sys := NewSystemModule(nil, newMockSystemAPI())
sys := NewSystemModule(nil, newMockSystemAPI(), nil, nil)

res := new(string)
err := sys.Version(nil, nil, res)
Expand All @@ -224,10 +225,57 @@ func TestSystemModule_Version(t *testing.T) {
}

func TestSystemModule_Properties(t *testing.T) {
sys := NewSystemModule(nil, newMockSystemAPI())
sys := NewSystemModule(nil, newMockSystemAPI(), nil, nil)

res := new(interface{})
err := sys.Properties(nil, nil, res)
require.NoError(t, err)
require.Equal(t, testSystemInfo.SystemProperties, *res)
}

func TestSystemModule_AccountNextIndex(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

is it possible to add a test for the non-pending case?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, I've added tests.

sys := setupSystemModule(t)
expected := U64Response(uint64(10))

res := new(U64Response)
req := StringRequest{
String: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
}
err := sys.AccountNextIndex(nil, &req, res)
require.NoError(t, err)

require.Equal(t, expected, *res)

}

func setupSystemModule(t *testing.T) *SystemModule {
// setup service
net := newNetworkService(t)
chain := newTestStateService(t)
// init storage with test data
ts, err := chain.Storage.TrieState(nil)
require.NoError(t, err)

aliceAcctStoKey, err := common.HexToBytes("0x26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da9de1e86a9a8c739864cf3cc5ec2bea59fd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d")
require.NoError(t, err)
aliceAcctInfo := types.AccountInfo{
Nonce: 10,
RefCount: 0,
Data: struct {
Free common.Uint128
Reserved common.Uint128
MiscFrozen common.Uint128
FreeFrozen common.Uint128
}{},
}
aliceAcctEncoded, err := scale.Encode(aliceAcctInfo)
require.NoError(t, err)
err = ts.Set(aliceAcctStoKey, aliceAcctEncoded)
require.NoError(t, err)

err = chain.Storage.StoreTrie(ts)
require.NoError(t, err)

core := newCoreService(t, chain)
return NewSystemModule(net, nil, core, chain.Storage)
}
4 changes: 2 additions & 2 deletions dot/rpc/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ func TestNewService(t *testing.T) {
}

func TestService_Methods(t *testing.T) {
qtySystemMethods := 9
qtySystemMethods := 10
qtyRPCMethods := 1
qtyAuthorMethods := 7

rpcService := NewService()
sysMod := modules.NewSystemModule(nil, nil)
sysMod := modules.NewSystemModule(nil, nil, nil, nil)
rpcService.BuildMethodNames(sysMod, "system")
m := rpcService.Methods()
require.Equal(t, qtySystemMethods, len(m)) // check to confirm quantity for methods is correct
Expand Down