Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
2 changes: 1 addition & 1 deletion chain/dev/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,5 @@ enabled = true
ws = true
port = 8545
host = "localhost"
modules = ["system", "author", "chain", "state", "rpc", "grandpa"]
modules = ["system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate"]
ws-port = 8546
2 changes: 1 addition & 1 deletion chain/dev/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ var (
// DefaultRPCHTTPPort rpc port
DefaultRPCHTTPPort = uint32(8545)
// DefaultRPCModules rpc modules
DefaultRPCModules = []string{"system", "author", "chain", "state", "rpc", "grandpa"}
DefaultRPCModules = []string{"system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate"}
// DefaultRPCWSPort rpc websocket port
DefaultRPCWSPort = uint32(8546)
// DefaultRPCEnabled enables the RPC server
Expand Down
2 changes: 1 addition & 1 deletion chain/gssmr/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,5 @@ discovery-interval = 10
enabled = false
port = 8545
host = "localhost"
modules = ["system", "author", "chain", "state", "rpc", "grandpa"]
modules = ["system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate"]
ws-port = 8546
2 changes: 1 addition & 1 deletion chain/gssmr/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ var (
// DefaultRPCHTTPPort rpc port
DefaultRPCHTTPPort = uint32(8545)
// DefaultRPCModules rpc modules
DefaultRPCModules = []string{"system", "author", "chain", "state", "rpc", "grandpa"}
DefaultRPCModules = []string{"system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate"}
// DefaultRPCWSPort rpc websocket port
DefaultRPCWSPort = uint32(8546)
)
2 changes: 1 addition & 1 deletion chain/kusama/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ enabled = false
external = false
port = 8545
host = "localhost"
modules = ["system", "author", "chain", "state", "rpc", "grandpa"]
modules = ["system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate"]
ws-port = 8546
ws = false
ws-external = false
2 changes: 1 addition & 1 deletion chain/kusama/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ var (
// DefaultRPCHTTPPort rpc port
DefaultRPCHTTPPort = uint32(8545)
// DefaultRPCModules rpc modules
DefaultRPCModules = []string{"system", "author", "chain", "state", "rpc", "grandpa"}
DefaultRPCModules = []string{"system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate"}
// DefaultRPCWSPort rpc websocket port
DefaultRPCWSPort = uint32(8546)
)
2 changes: 1 addition & 1 deletion chain/polkadot/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ nomdns = false
enabled = false
port = 8545
host = "localhost"
modules = ["system", "author", "chain", "state", "rpc", "grandpa"]
modules = ["system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate"]
ws-port = 8546
2 changes: 1 addition & 1 deletion chain/polkadot/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ var (
// DefaultRPCHTTPPort rpc port
DefaultRPCHTTPPort = uint32(8545)
// DefaultRPCModules rpc modules
DefaultRPCModules = []string{"system", "author", "chain", "state", "rpc", "grandpa"}
DefaultRPCModules = []string{"system", "author", "chain", "state", "rpc", "grandpa", "offchain", "childstate"}
// DefaultRPCWSPort rpc websocket port
DefaultRPCWSPort = uint32(8546)
)
2 changes: 2 additions & 0 deletions dot/rpc/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ func (h *HTTPServer) RegisterModules(mods []string) {
srvc = modules.NewDevModule(h.serverConfig.BlockProducerAPI, h.serverConfig.NetworkAPI)
case "offchain":
srvc = modules.NewOffchainModule(h.serverConfig.NodeStorage)
case "childstate":
srvc = modules.NewChildStateModule(h.serverConfig.StorageAPI, h.serverConfig.BlockAPI)
default:
h.logger.Warn("Unrecognised module", "module", mod)
continue
Expand Down
2 changes: 2 additions & 0 deletions dot/rpc/modules/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ import (
"github.com/ChainSafe/gossamer/lib/grandpa"
"github.com/ChainSafe/gossamer/lib/runtime"
"github.com/ChainSafe/gossamer/lib/transaction"
"github.com/ChainSafe/gossamer/lib/trie"
)

// StorageAPI is the interface for the storage state
type StorageAPI interface {
GetStorage(root *common.Hash, key []byte) ([]byte, error)
GetStorageChild(root *common.Hash, keyToChild []byte) (*trie.Trie, error)
GetStorageByBlockHash(bhash common.Hash, key []byte) ([]byte, error)
Entries(root *common.Hash) (map[string][]byte, error)
GetStateRootFromBlock(bhash *common.Hash) (*common.Hash, error)
Expand Down
55 changes: 55 additions & 0 deletions dot/rpc/modules/childstate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package modules

import (
"net/http"

"github.com/ChainSafe/gossamer/lib/common"
)

// GetKeysRequest represents the request to retrieve the keys of a child storage
type GetKeysRequest struct {
Key []byte
Prefix []byte
Hash common.Hash
}

// ChildStateModule is the module responsible to implement all the childstate RPC calls
type ChildStateModule struct {
storageAPI StorageAPI
blockAPI BlockAPI
}

// NewChildStateModule returns a new ChildStateModule
func NewChildStateModule(s StorageAPI, b BlockAPI) *ChildStateModule {
return &ChildStateModule{
storageAPI: s,
blockAPI: b,
}
}

// GetKeys returns the keys from the specified child storage. The keys can also be filtered based on a prefix.
func (cs *ChildStateModule) GetKeys(_ *http.Request, req *GetKeysRequest, res *[]string) error {
if req.Hash == common.EmptyHash {
req.Hash = cs.blockAPI.BestBlockHash()
}

stateRoot, err := cs.storageAPI.GetStateRootFromBlock(&req.Hash)
if err != nil {
return err
}

trie, err := cs.storageAPI.GetStorageChild(stateRoot, req.Key)
if err != nil {
return err
}

keys := trie.GetKeysWithPrefix(req.Prefix)
hexKeys := make([]string, len(keys))
for idx, k := range keys {
hex := common.BytesToHex(k)
hexKeys[idx] = hex
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
hex := common.BytesToHex(k)
hexKeys[idx] = hex
hexKeys[idx] = common.BytesToHex(k)

}

*res = hexKeys
return nil
}
96 changes: 96 additions & 0 deletions dot/rpc/modules/childstate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package modules

import (
"math/big"
"testing"

"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/trie"
"github.com/stretchr/testify/require"
)

func TestChildStateGetKeys(t *testing.T) {
childStateModule, currBlockHash := setupChildStateStorage(t)

req := &GetKeysRequest{
Key: []byte(":child_storage_key"),
Prefix: []byte{},
Hash: common.EmptyHash,
}

res := make([]string, 0)
err := childStateModule.GetKeys(nil, req, &res)
require.NoError(t, err)
require.Len(t, res, 3)

for _, r := range res {
b, dErr := common.HexToBytes(r)
require.NoError(t, dErr)
require.Contains(t, []string{
":child_first", ":child_second", ":another_child",
}, string(b))
}

req = &GetKeysRequest{
Key: []byte(":child_storage_key"),
Prefix: []byte(":child_"),
Hash: currBlockHash,
}

err = childStateModule.GetKeys(nil, req, &res)
require.NoError(t, err)
require.Len(t, res, 2)

for _, r := range res {
b, err := common.HexToBytes(r)
require.NoError(t, err)
require.Contains(t, []string{
":child_first", ":child_second",
}, string(b))
}
}

func setupChildStateStorage(t *testing.T) (*ChildStateModule, common.Hash) {
t.Helper()

st := newTestStateService(t)

tr, err := st.Storage.TrieState(nil)
require.NoError(t, err)

tr.Set([]byte(":first_key"), []byte(":value1"))
tr.Set([]byte(":second_key"), []byte(":second_value"))

childTr := trie.NewEmptyTrie()
childTr.Put([]byte(":child_first"), []byte(":child_first_value"))
childTr.Put([]byte(":child_second"), []byte(":child_second_value"))
childTr.Put([]byte(":another_child"), []byte("value"))

err = tr.SetChild([]byte(":child_storage_key"), childTr)
require.NoError(t, err)

stateRoot, err := tr.Root()
require.NoError(t, err)

bb, err := st.Block.BestBlock()
require.NoError(t, err)

err = st.Storage.StoreTrie(tr, nil)
require.NoError(t, err)

b := &types.Block{
Header: types.Header{
ParentHash: bb.Header.Hash(),
Number: big.NewInt(0).Add(big.NewInt(1), bb.Header.Number),
StateRoot: stateRoot,
},
Body: []byte{},
}

err = st.Block.AddBlock(b)
require.NoError(t, err)

hash, _ := st.Block.GetBlockHash(b.Header.Number)
return NewChildStateModule(st.Storage, st.Block), hash
}
27 changes: 26 additions & 1 deletion dot/rpc/modules/mocks/storage_api.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.