Skip to content

Commit

Permalink
feat(rpc): Implement childstate_getStorageHash RPC call (ChainSafe#…
Browse files Browse the repository at this point in the history
…1805)

* feat: implement childstate_getKeys

* chore: finish unit tests

* chore: add childstate to http.go module init

* chore: address lint warns

* chore: addressing test issues

* chore: address deepsource complaints

* feat: implement childstate_getStorageHash

* chore: fix export comment

* chore: update hash to be a pointer

* chore: resolve nil pointer
  • Loading branch information
EclesioMeloJunior authored and timwu20 committed Dec 6, 2021
1 parent 00c4951 commit 0e31754
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 6 deletions.
1 change: 1 addition & 0 deletions dot/rpc/modules/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
type StorageAPI interface {
GetStorage(root *common.Hash, key []byte) ([]byte, error)
GetStorageChild(root *common.Hash, keyToChild []byte) (*trie.Trie, error)
GetStorageFromChild(root *common.Hash, keyToChild, key []byte) ([]byte, 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
1 change: 1 addition & 0 deletions dot/rpc/modules/chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ func newTestStateService(t *testing.T) *state.Service {
stateSrvc.UseMemDB()

gen, genTrie, genesisHeader := genesis.NewTestGenesisWithTrieAndHeader(t)

err = stateSrvc.Initialise(gen, genesisHeader, genTrie)
require.NoError(t, err)

Expand Down
46 changes: 42 additions & 4 deletions dot/rpc/modules/childstate.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,14 @@ import (
type GetKeysRequest struct {
Key []byte
Prefix []byte
Hash common.Hash
Hash *common.Hash
}

// GetStorageHash the request to get the entry child storage hash
type GetStorageHash struct {
KeyChild []byte
EntryKey []byte
Hash *common.Hash
}

// ChildStateModule is the module responsible to implement all the childstate RPC calls
Expand All @@ -45,11 +52,15 @@ func NewChildStateModule(s StorageAPI, b BlockAPI) *ChildStateModule {

// 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()
var hash common.Hash

if req.Hash == nil {
hash = cs.blockAPI.BestBlockHash()
} else {
hash = *req.Hash
}

stateRoot, err := cs.storageAPI.GetStateRootFromBlock(&req.Hash)
stateRoot, err := cs.storageAPI.GetStateRootFromBlock(&hash)
if err != nil {
return err
}
Expand All @@ -68,3 +79,30 @@ func (cs *ChildStateModule) GetKeys(_ *http.Request, req *GetKeysRequest, res *[
*res = hexKeys
return nil
}

// GetStorageHash returns the hash of a child storage entry
func (cs *ChildStateModule) GetStorageHash(_ *http.Request, req *GetStorageHash, res *string) error {
var hash common.Hash

if req.Hash == nil {
hash = cs.blockAPI.BestBlockHash()
} else {
hash = *req.Hash
}

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

item, err := cs.storageAPI.GetStorageFromChild(stateRoot, req.KeyChild, req.EntryKey)
if err != nil {
return err
}

if item != nil {
*res = common.BytesToHash(item).String()
}

return nil
}
67 changes: 65 additions & 2 deletions dot/rpc/modules/childstate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@
package modules

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

"github.com/ChainSafe/chaindb"

"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/trie"
Expand All @@ -32,7 +35,7 @@ func TestChildStateGetKeys(t *testing.T) {
req := &GetKeysRequest{
Key: []byte(":child_storage_key"),
Prefix: []byte{},
Hash: common.EmptyHash,
Hash: nil,
}

res := make([]string, 0)
Expand All @@ -51,7 +54,7 @@ func TestChildStateGetKeys(t *testing.T) {
req = &GetKeysRequest{
Key: []byte(":child_storage_key"),
Prefix: []byte(":child_"),
Hash: currBlockHash,
Hash: &currBlockHash,
}

err = childStateModule.GetKeys(nil, req, &res)
Expand All @@ -67,6 +70,66 @@ func TestChildStateGetKeys(t *testing.T) {
}
}

func TestGetStorageHash(t *testing.T) {
mod, blockHash := setupChildStateStorage(t)
invalidBlockHash := common.BytesToHash([]byte("invalid block hash"))

tests := []struct {
expect string
err error
hash *common.Hash
keyChild []byte
entry []byte
}{
{
err: nil,
expect: common.BytesToHash([]byte(":child_first_value")).String(),
hash: nil,
entry: []byte(":child_first"),
keyChild: []byte(":child_storage_key"),
},
{
err: nil,
expect: common.BytesToHash([]byte(":child_second_value")).String(),
hash: &blockHash,
entry: []byte(":child_second"),
keyChild: []byte(":child_storage_key"),
},
{
err: fmt.Errorf("child trie does not exist at key %s%s", trie.ChildStorageKeyPrefix, []byte(":not_exist")),
hash: &blockHash,
entry: []byte(":child_second"),
keyChild: []byte(":not_exist"),
},
{
err: chaindb.ErrKeyNotFound,
hash: &invalidBlockHash,
},
}

for _, test := range tests {
var req GetStorageHash
var res string

req.Hash = test.hash
req.EntryKey = test.entry
req.KeyChild = test.keyChild

err := mod.GetStorageHash(nil, &req, &res)

if test.err != nil {
require.Error(t, err)
require.Equal(t, err, test.err)
} else {
require.NoError(t, err)
}

if test.expect != "" {
require.Equal(t, test.expect, res)
}
}
}

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

Expand Down
23 changes: 23 additions & 0 deletions dot/rpc/modules/mocks/storage_api.go

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

0 comments on commit 0e31754

Please sign in to comment.