From ad38cc292d7a28696e21a05bbc8967cca989d9f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ecl=C3=A9sio=20J=C3=BAnior?= Date: Tue, 21 Sep 2021 09:52:47 -0400 Subject: [PATCH 01/10] feat: implement childstate_getKeys --- dot/rpc/modules/api.go | 2 ++ dot/rpc/modules/childstate.go | 44 ++++++++++++++++++++++++++++ dot/rpc/modules/mocks/storage_api.go | 27 ++++++++++++++++- 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 dot/rpc/modules/childstate.go diff --git a/dot/rpc/modules/api.go b/dot/rpc/modules/api.go index c9d4da2e2c..b0ed6e0e46 100644 --- a/dot/rpc/modules/api.go +++ b/dot/rpc/modules/api.go @@ -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) diff --git a/dot/rpc/modules/childstate.go b/dot/rpc/modules/childstate.go new file mode 100644 index 0000000000..8d06243523 --- /dev/null +++ b/dot/rpc/modules/childstate.go @@ -0,0 +1,44 @@ +package modules + +import ( + "net/http" + + "github.com/ChainSafe/gossamer/lib/common" +) + +type GetKeysRequest struct { + Key string + Prefix string + Hash common.Hash +} + +type ChildStateModule struct { + storageAPI StorageAPI + blockAPI BlockAPI +} + +func (cs *ChildStateModule) GetKeys(r *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, []byte(req.Key)) + if err != nil { + return err + } + + keys := trie.GetKeysWithPrefix([]byte(req.Hash.String())) + hexKeys := make([]string, len(keys)) + for idx, k := range keys { + hex := common.BytesToHex(k) + hexKeys[idx] = hex + } + + *res = hexKeys + return nil +} diff --git a/dot/rpc/modules/mocks/storage_api.go b/dot/rpc/modules/mocks/storage_api.go index db3bb22a4e..d61fd3e2a1 100644 --- a/dot/rpc/modules/mocks/storage_api.go +++ b/dot/rpc/modules/mocks/storage_api.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.8.0. DO NOT EDIT. +// Code generated by mockery v0.0.0-dev. DO NOT EDIT. package mocks @@ -7,6 +7,8 @@ import ( mock "github.com/stretchr/testify/mock" state "github.com/ChainSafe/gossamer/dot/state" + + trie "github.com/ChainSafe/gossamer/lib/trie" ) // MockStorageAPI is an autogenerated mock type for the StorageAPI type @@ -129,6 +131,29 @@ func (_m *MockStorageAPI) GetStorageByBlockHash(bhash common.Hash, key []byte) ( return r0, r1 } +// GetStorageChild provides a mock function with given fields: root, keyToChild +func (_m *MockStorageAPI) GetStorageChild(root *common.Hash, keyToChild []byte) (*trie.Trie, error) { + ret := _m.Called(root, keyToChild) + + var r0 *trie.Trie + if rf, ok := ret.Get(0).(func(*common.Hash, []byte) *trie.Trie); ok { + r0 = rf(root, keyToChild) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*trie.Trie) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(*common.Hash, []byte) error); ok { + r1 = rf(root, keyToChild) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // RegisterStorageObserver provides a mock function with given fields: observer func (_m *MockStorageAPI) RegisterStorageObserver(observer state.Observer) { _m.Called(observer) From 7b634a17dd1b11dcb34d31440a35d1af1c036ab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ecl=C3=A9sio=20J=C3=BAnior?= Date: Tue, 21 Sep 2021 19:04:22 -0400 Subject: [PATCH 02/10] chore: finish unit tests --- dot/rpc/modules/childstate.go | 15 +++-- dot/rpc/modules/childstate_test.go | 96 ++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 dot/rpc/modules/childstate_test.go diff --git a/dot/rpc/modules/childstate.go b/dot/rpc/modules/childstate.go index 8d06243523..1bdf1c0392 100644 --- a/dot/rpc/modules/childstate.go +++ b/dot/rpc/modules/childstate.go @@ -7,8 +7,8 @@ import ( ) type GetKeysRequest struct { - Key string - Prefix string + Key []byte + Prefix []byte Hash common.Hash } @@ -17,6 +17,13 @@ type ChildStateModule struct { blockAPI BlockAPI } +func NewChildStateModule(s StorageAPI, b BlockAPI) *ChildStateModule { + return &ChildStateModule{ + storageAPI: s, + blockAPI: b, + } +} + func (cs *ChildStateModule) GetKeys(r *http.Request, req *GetKeysRequest, res *[]string) error { if req.Hash == common.EmptyHash { req.Hash = cs.blockAPI.BestBlockHash() @@ -27,12 +34,12 @@ func (cs *ChildStateModule) GetKeys(r *http.Request, req *GetKeysRequest, res *[ return err } - trie, err := cs.storageAPI.GetStorageChild(stateRoot, []byte(req.Key)) + trie, err := cs.storageAPI.GetStorageChild(stateRoot, req.Key) if err != nil { return err } - keys := trie.GetKeysWithPrefix([]byte(req.Hash.String())) + keys := trie.GetKeysWithPrefix(req.Prefix) hexKeys := make([]string, len(keys)) for idx, k := range keys { hex := common.BytesToHex(k) diff --git a/dot/rpc/modules/childstate_test.go b/dot/rpc/modules/childstate_test.go new file mode 100644 index 0000000000..7097b496c0 --- /dev/null +++ b/dot/rpc/modules/childstate_test.go @@ -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, err := common.HexToBytes(r) + require.NoError(t, err) + 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: types.NewBody([]byte{}), + } + + err = st.Block.AddBlock(b) + require.NoError(t, err) + + hash, _ := st.Block.GetBlockHash(b.Header.Number) + return NewChildStateModule(st.Storage, st.Block), hash +} From 10e41bfaaa8c63b9c54882898a075632cabac2df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ecl=C3=A9sio=20J=C3=BAnior?= Date: Wed, 22 Sep 2021 10:46:28 -0400 Subject: [PATCH 03/10] chore: add childstate to http.go module init --- chain/dev/config.toml | 2 +- chain/gssmr/config.toml | 2 +- chain/kusama/config.toml | 2 +- chain/polkadot/config.toml | 2 +- dot/rpc/http.go | 2 ++ dot/rpc/modules/childstate_test.go | 4 ++-- 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/chain/dev/config.toml b/chain/dev/config.toml index 8c96c07e9e..f19fa63578 100644 --- a/chain/dev/config.toml +++ b/chain/dev/config.toml @@ -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 diff --git a/chain/gssmr/config.toml b/chain/gssmr/config.toml index 893fd41a1d..6419e8cec1 100644 --- a/chain/gssmr/config.toml +++ b/chain/gssmr/config.toml @@ -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 diff --git a/chain/kusama/config.toml b/chain/kusama/config.toml index 941df4b6e6..dd3261fde6 100644 --- a/chain/kusama/config.toml +++ b/chain/kusama/config.toml @@ -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 diff --git a/chain/polkadot/config.toml b/chain/polkadot/config.toml index 6ec7c85783..98f3291e1b 100644 --- a/chain/polkadot/config.toml +++ b/chain/polkadot/config.toml @@ -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 \ No newline at end of file diff --git a/dot/rpc/http.go b/dot/rpc/http.go index a7675d17e8..9440c65e58 100644 --- a/dot/rpc/http.go +++ b/dot/rpc/http.go @@ -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 diff --git a/dot/rpc/modules/childstate_test.go b/dot/rpc/modules/childstate_test.go index 7097b496c0..d94bc304c8 100644 --- a/dot/rpc/modules/childstate_test.go +++ b/dot/rpc/modules/childstate_test.go @@ -80,12 +80,12 @@ func setupChildStateStorage(t *testing.T) (*ChildStateModule, common.Hash) { require.NoError(t, err) b := &types.Block{ - Header: &types.Header{ + Header: types.Header{ ParentHash: bb.Header.Hash(), Number: big.NewInt(0).Add(big.NewInt(1), bb.Header.Number), StateRoot: stateRoot, }, - Body: types.NewBody([]byte{}), + Body: []byte{}, } err = st.Block.AddBlock(b) From b3ad509247704422d6167942dbd7ef545ad130b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ecl=C3=A9sio=20J=C3=BAnior?= Date: Wed, 22 Sep 2021 15:25:44 -0400 Subject: [PATCH 04/10] chore: address lint warns --- dot/rpc/modules/childstate.go | 4 ++++ dot/rpc/modules/childstate_test.go | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/dot/rpc/modules/childstate.go b/dot/rpc/modules/childstate.go index 1bdf1c0392..d5ae86a0eb 100644 --- a/dot/rpc/modules/childstate.go +++ b/dot/rpc/modules/childstate.go @@ -6,17 +6,20 @@ import ( "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, @@ -24,6 +27,7 @@ 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(r *http.Request, req *GetKeysRequest, res *[]string) error { if req.Hash == common.EmptyHash { req.Hash = cs.blockAPI.BestBlockHash() diff --git a/dot/rpc/modules/childstate_test.go b/dot/rpc/modules/childstate_test.go index d94bc304c8..5fbbf37a42 100644 --- a/dot/rpc/modules/childstate_test.go +++ b/dot/rpc/modules/childstate_test.go @@ -25,8 +25,8 @@ func TestChildStateGetKeys(t *testing.T) { require.Len(t, res, 3) for _, r := range res { - b, err := common.HexToBytes(r) - require.NoError(t, err) + b, dErr := common.HexToBytes(r) + require.NoError(t, dErr) require.Contains(t, []string{ ":child_first", ":child_second", ":another_child", }, string(b)) From 99b8f3027fbb9793699608153ca8b2afcacfff7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ecl=C3=A9sio=20J=C3=BAnior?= Date: Wed, 22 Sep 2021 15:29:57 -0400 Subject: [PATCH 05/10] chore: addressing test issues --- chain/dev/defaults.go | 2 +- chain/gssmr/defaults.go | 2 +- chain/kusama/defaults.go | 2 +- chain/polkadot/defaults.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/chain/dev/defaults.go b/chain/dev/defaults.go index 38b6fa7111..b6db6ec7e9 100644 --- a/chain/dev/defaults.go +++ b/chain/dev/defaults.go @@ -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 diff --git a/chain/gssmr/defaults.go b/chain/gssmr/defaults.go index b537f6b1a9..0cdc3d2d35 100644 --- a/chain/gssmr/defaults.go +++ b/chain/gssmr/defaults.go @@ -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) ) diff --git a/chain/kusama/defaults.go b/chain/kusama/defaults.go index e4c2e2657a..8902a46297 100644 --- a/chain/kusama/defaults.go +++ b/chain/kusama/defaults.go @@ -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) ) diff --git a/chain/polkadot/defaults.go b/chain/polkadot/defaults.go index 4af96caf0e..dbc0dd8e2b 100644 --- a/chain/polkadot/defaults.go +++ b/chain/polkadot/defaults.go @@ -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) ) From 08e9dc6da0ef062fb2b7aab236dfc3f8a2e88e99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ecl=C3=A9sio=20J=C3=BAnior?= Date: Wed, 22 Sep 2021 15:34:16 -0400 Subject: [PATCH 06/10] chore: address deepsource complaints --- dot/rpc/modules/childstate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dot/rpc/modules/childstate.go b/dot/rpc/modules/childstate.go index d5ae86a0eb..01eb10cda9 100644 --- a/dot/rpc/modules/childstate.go +++ b/dot/rpc/modules/childstate.go @@ -28,7 +28,7 @@ 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(r *http.Request, req *GetKeysRequest, res *[]string) error { +func (cs *ChildStateModule) GetKeys(_ *http.Request, req *GetKeysRequest, res *[]string) error { if req.Hash == common.EmptyHash { req.Hash = cs.blockAPI.BestBlockHash() } From e65857956e35e0467ee7b8cad1c909a7d5f0715a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ecl=C3=A9sio=20J=C3=BAnior?= Date: Thu, 23 Sep 2021 14:35:23 -0400 Subject: [PATCH 07/10] feat: implement childstate_getStorageHash --- dot/rpc/modules/api.go | 1 + dot/rpc/modules/childstate.go | 30 ++++++++++++++ dot/rpc/modules/childstate_test.go | 61 ++++++++++++++++++++++++++++ dot/rpc/modules/mocks/storage_api.go | 23 +++++++++++ 4 files changed, 115 insertions(+) diff --git a/dot/rpc/modules/api.go b/dot/rpc/modules/api.go index b0ed6e0e46..7f161c3153 100644 --- a/dot/rpc/modules/api.go +++ b/dot/rpc/modules/api.go @@ -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) diff --git a/dot/rpc/modules/childstate.go b/dot/rpc/modules/childstate.go index 01eb10cda9..3b254a02de 100644 --- a/dot/rpc/modules/childstate.go +++ b/dot/rpc/modules/childstate.go @@ -13,6 +13,13 @@ type GetKeysRequest struct { Hash common.Hash } +// GetKeysRequest 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 type ChildStateModule struct { storageAPI StorageAPI @@ -53,3 +60,26 @@ 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 { + if req.Hash == common.EmptyHash { + req.Hash = cs.blockAPI.BestBlockHash() + } + + stateRoot, err := cs.storageAPI.GetStateRootFromBlock(&req.Hash) + if err != nil { + return err + } + + item, err := cs.storageAPI.GetStorageFromChild(stateRoot, req.KeyChild, req.EntryKey) + if err != nil { + return err + } + + if len(item) > 0 { + *res = common.BytesToHash(item).String() + } + + return nil +} diff --git a/dot/rpc/modules/childstate_test.go b/dot/rpc/modules/childstate_test.go index 5fbbf37a42..b3ce3b0fac 100644 --- a/dot/rpc/modules/childstate_test.go +++ b/dot/rpc/modules/childstate_test.go @@ -1,9 +1,11 @@ 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" @@ -51,6 +53,65 @@ func TestChildStateGetKeys(t *testing.T) { } } +func TestGetStorageHash(t *testing.T) { + mod, blockHash := setupChildStateStorage(t) + + tests := []struct { + expect string + err error + hash common.Hash + keyChild []byte + entry []byte + }{ + { + err: nil, + expect: common.BytesToHash([]byte(":child_first_value")).String(), + hash: common.EmptyHash, + 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: common.BytesToHash([]byte("invalid block hash")), + }, + } + + 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() diff --git a/dot/rpc/modules/mocks/storage_api.go b/dot/rpc/modules/mocks/storage_api.go index d61fd3e2a1..1135575e89 100644 --- a/dot/rpc/modules/mocks/storage_api.go +++ b/dot/rpc/modules/mocks/storage_api.go @@ -154,6 +154,29 @@ func (_m *MockStorageAPI) GetStorageChild(root *common.Hash, keyToChild []byte) return r0, r1 } +// GetStorageFromChild provides a mock function with given fields: root, keyToChild, key +func (_m *MockStorageAPI) GetStorageFromChild(root *common.Hash, keyToChild []byte, key []byte) ([]byte, error) { + ret := _m.Called(root, keyToChild, key) + + var r0 []byte + if rf, ok := ret.Get(0).(func(*common.Hash, []byte, []byte) []byte); ok { + r0 = rf(root, keyToChild, key) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(*common.Hash, []byte, []byte) error); ok { + r1 = rf(root, keyToChild, key) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // RegisterStorageObserver provides a mock function with given fields: observer func (_m *MockStorageAPI) RegisterStorageObserver(observer state.Observer) { _m.Called(observer) From d18dc613eab1153e8b8f4ae93f4a71b588f6e304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ecl=C3=A9sio=20J=C3=BAnior?= Date: Tue, 28 Sep 2021 10:49:43 -0400 Subject: [PATCH 08/10] chore: fix export comment --- dot/rpc/modules/childstate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dot/rpc/modules/childstate.go b/dot/rpc/modules/childstate.go index 0b10be0939..ca6227a569 100644 --- a/dot/rpc/modules/childstate.go +++ b/dot/rpc/modules/childstate.go @@ -29,7 +29,7 @@ type GetKeysRequest struct { Hash common.Hash } -// GetKeysRequest the request to get the entry child storage hash +// GetStorageHash the request to get the entry child storage hash type GetStorageHash struct { KeyChild []byte EntryKey []byte From d16c3065deb29e0ac6755e7e94da52ebc35fc75b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ecl=C3=A9sio=20J=C3=BAnior?= Date: Thu, 30 Sep 2021 15:12:14 -0400 Subject: [PATCH 09/10] chore: update hash to be a pointer --- dot/rpc/modules/childstate.go | 18 +++++++++--------- dot/rpc/modules/childstate_test.go | 15 ++++++++------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/dot/rpc/modules/childstate.go b/dot/rpc/modules/childstate.go index ca6227a569..30be8a6b53 100644 --- a/dot/rpc/modules/childstate.go +++ b/dot/rpc/modules/childstate.go @@ -26,14 +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 + Hash *common.Hash } // ChildStateModule is the module responsible to implement all the childstate RPC calls @@ -52,11 +52,11 @@ 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() + if req.Hash == nil { + *req.Hash = cs.blockAPI.BestBlockHash() } - stateRoot, err := cs.storageAPI.GetStateRootFromBlock(&req.Hash) + stateRoot, err := cs.storageAPI.GetStateRootFromBlock(req.Hash) if err != nil { return err } @@ -78,11 +78,11 @@ func (cs *ChildStateModule) GetKeys(_ *http.Request, req *GetKeysRequest, res *[ // GetStorageHash returns the hash of a child storage entry func (cs *ChildStateModule) GetStorageHash(_ *http.Request, req *GetStorageHash, res *string) error { - if req.Hash == common.EmptyHash { - req.Hash = cs.blockAPI.BestBlockHash() + if req.Hash == nil { + *req.Hash = cs.blockAPI.BestBlockHash() } - stateRoot, err := cs.storageAPI.GetStateRootFromBlock(&req.Hash) + stateRoot, err := cs.storageAPI.GetStateRootFromBlock(req.Hash) if err != nil { return err } @@ -92,7 +92,7 @@ func (cs *ChildStateModule) GetStorageHash(_ *http.Request, req *GetStorageHash, return err } - if len(item) > 0 { + if item != nil { *res = common.BytesToHash(item).String() } diff --git a/dot/rpc/modules/childstate_test.go b/dot/rpc/modules/childstate_test.go index 40f3508601..c46081698f 100644 --- a/dot/rpc/modules/childstate_test.go +++ b/dot/rpc/modules/childstate_test.go @@ -35,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) @@ -54,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) @@ -72,37 +72,38 @@ 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 + hash *common.Hash keyChild []byte entry []byte }{ { err: nil, expect: common.BytesToHash([]byte(":child_first_value")).String(), - hash: common.EmptyHash, + hash: nil, entry: []byte(":child_first"), keyChild: []byte(":child_storage_key"), }, { err: nil, expect: common.BytesToHash([]byte(":child_second_value")).String(), - hash: blockHash, + 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, + hash: &blockHash, entry: []byte(":child_second"), keyChild: []byte(":not_exist"), }, { err: chaindb.ErrKeyNotFound, - hash: common.BytesToHash([]byte("invalid block hash")), + hash: &invalidBlockHash, }, } From 12f767587bb23c375b83ad9242450b6a0a8b8f3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ecl=C3=A9sio=20J=C3=BAnior?= Date: Thu, 30 Sep 2021 15:50:49 -0400 Subject: [PATCH 10/10] chore: resolve nil pointer --- dot/rpc/modules/chain_test.go | 1 + dot/rpc/modules/childstate.go | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/dot/rpc/modules/chain_test.go b/dot/rpc/modules/chain_test.go index 73254f5849..141f7cdb11 100644 --- a/dot/rpc/modules/chain_test.go +++ b/dot/rpc/modules/chain_test.go @@ -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) diff --git a/dot/rpc/modules/childstate.go b/dot/rpc/modules/childstate.go index 30be8a6b53..361afe1fea 100644 --- a/dot/rpc/modules/childstate.go +++ b/dot/rpc/modules/childstate.go @@ -52,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 { + var hash common.Hash + if req.Hash == nil { - *req.Hash = cs.blockAPI.BestBlockHash() + 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 } @@ -78,11 +82,15 @@ func (cs *ChildStateModule) GetKeys(_ *http.Request, req *GetKeysRequest, res *[ // 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 { - *req.Hash = cs.blockAPI.BestBlockHash() + 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 }