Skip to content

Commit

Permalink
fix(dot/rpc/modules): rpc.state.queryStorage fixed (#2565)
Browse files Browse the repository at this point in the history
  • Loading branch information
edwardmack authored Jul 19, 2022
1 parent b3698d7 commit 1ec0d47
Show file tree
Hide file tree
Showing 19 changed files with 595 additions and 510 deletions.
55 changes: 0 additions & 55 deletions dot/core/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -560,61 +560,6 @@ func (s *Service) GetMetadata(bhash *common.Hash) ([]byte, error) {
return rt.Metadata()
}

// QueryStorage returns the key-value data by block based on `keys` params
// on every block starting `from` until `to` block, if `to` is not nil
func (s *Service) QueryStorage(from, to common.Hash, keys ...string) (map[common.Hash]QueryKeyValueChanges, error) {
if to.IsEmpty() {
to = s.blockState.BestBlockHash()
}

blocksToQuery, err := s.blockState.SubChain(from, to)
if err != nil {
return nil, err
}

queries := make(map[common.Hash]QueryKeyValueChanges)

for _, hash := range blocksToQuery {
changes, err := s.tryQueryStorage(hash, keys...)
if err != nil {
return nil, err
}

queries[hash] = changes
}

return queries, nil
}

// tryQueryStorage will try to get all the `keys` inside the block's current state
func (s *Service) tryQueryStorage(block common.Hash, keys ...string) (QueryKeyValueChanges, error) {
stateRootHash, err := s.storageState.GetStateRootFromBlock(&block)
if err != nil {
return nil, err
}

changes := make(QueryKeyValueChanges)
for _, k := range keys {
keyBytes, err := common.HexToBytes(k)
if err != nil {
return nil, err
}

storedData, err := s.storageState.GetStorage(stateRootHash, keyBytes)
if err != nil {
return nil, err
}

if storedData == nil {
continue
}

changes[k] = common.BytesToHex(storedData)
}

return changes, nil
}

// GetReadProofAt will return an array with the proofs for the keys passed as params
// based on the block hash passed as param as well, if block hash is nil then the current state will take place
func (s *Service) GetReadProofAt(block common.Hash, keys [][]byte) (
Expand Down
204 changes: 0 additions & 204 deletions dot/core/service_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ import (

//go:generate mockgen -destination=mock_telemetry_test.go -package $GOPACKAGE github.com/ChainSafe/gossamer/dot/telemetry Client

const testSlotNumber = 21

func balanceKey(t *testing.T, pub []byte) (bKey []byte) {
t.Helper()

Expand All @@ -53,35 +51,6 @@ func balanceKey(t *testing.T, pub []byte) (bKey []byte) {
return
}

func newTestDigest(t *testing.T, slotNumber uint64) scale.VaryingDataTypeSlice {
t.Helper()
testBabeDigest := types.NewBabeDigest()
err := testBabeDigest.Set(types.BabeSecondaryPlainPreDigest{
AuthorityIndex: 17,
SlotNumber: slotNumber,
})
require.NoError(t, err)
data, err := scale.Marshal(testBabeDigest)
require.NoError(t, err)
vdts := types.NewDigest()
err = vdts.Add(
types.PreRuntimeDigest{
ConsensusEngineID: types.BabeEngineID,
Data: data,
},
types.ConsensusDigest{
ConsensusEngineID: types.BabeEngineID,
Data: data,
},
types.SealDigest{
ConsensusEngineID: types.BabeEngineID,
Data: data,
},
)
require.NoError(t, err)
return vdts
}

func generateTestValidRemarkTxns(t *testing.T, pubKey []byte, accInfo types.AccountInfo) ([]byte, runtime.Instance) {
t.Helper()
projectRootPath := filepath.Join(utils.GetProjectRootPathTest(t), "chain/gssmr/genesis.json")
Expand Down Expand Up @@ -759,176 +728,3 @@ func TestService_HandleRuntimeChangesAfterCodeSubstitutes(t *testing.T) {
rt.GetCodeHash(),
"expected different code hash after runtime update")
}

func TestTryQueryStore_WhenThereIsDataToRetrieve(t *testing.T) {
s := NewTestService(t, nil)
storageStateTrie, err := rtstorage.NewTrieState(trie.NewTrie(nil))

testKey, testValue := []byte("to"), []byte("0x1723712318238AB12312")
storageStateTrie.Set(testKey, testValue)
require.NoError(t, err)

digest := newTestDigest(t, testSlotNumber)
header, err := types.NewHeader(s.blockState.GenesisHash(), storageStateTrie.MustRoot(), common.Hash{}, 1, digest)

require.NoError(t, err)

err = s.storageState.StoreTrie(storageStateTrie, header)
require.NoError(t, err)

testBlock := &types.Block{
Header: *header,
Body: *types.NewBody([]types.Extrinsic{}),
}

err = s.blockState.AddBlock(testBlock)
require.NoError(t, err)

blockhash := testBlock.Header.Hash()
hexKey := common.BytesToHex(testKey)
keys := []string{hexKey}

changes, err := s.tryQueryStorage(blockhash, keys...)
require.NoError(t, err)

require.Equal(t, changes[hexKey], common.BytesToHex(testValue))
}

func TestTryQueryStore_WhenDoesNotHaveDataToRetrieve(t *testing.T) {
s := NewTestService(t, nil)
storageStateTrie, err := rtstorage.NewTrieState(trie.NewTrie(nil))
require.NoError(t, err)

digest := newTestDigest(t, testSlotNumber)
header, err := types.NewHeader(s.blockState.GenesisHash(), storageStateTrie.MustRoot(), common.Hash{}, 1, digest)
require.NoError(t, err)

err = s.storageState.StoreTrie(storageStateTrie, header)
require.NoError(t, err)

testBlock := &types.Block{
Header: *header,
Body: *types.NewBody([]types.Extrinsic{}),
}

err = s.blockState.AddBlock(testBlock)
require.NoError(t, err)

testKey := []byte("to")
blockhash := testBlock.Header.Hash()
hexKey := common.BytesToHex(testKey)
keys := []string{hexKey}

changes, err := s.tryQueryStorage(blockhash, keys...)
require.NoError(t, err)

require.Empty(t, changes)
}

func TestTryQueryState_WhenDoesNotHaveStateRoot(t *testing.T) {
s := NewTestService(t, nil)

digest := newTestDigest(t, testSlotNumber)
header, err := types.NewHeader(
s.blockState.GenesisHash(),
common.Hash{}, common.Hash{}, 1, digest)
require.NoError(t, err)

testBlock := &types.Block{
Header: *header,
Body: *types.NewBody([]types.Extrinsic{}),
}

err = s.blockState.AddBlock(testBlock)
require.NoError(t, err)

testKey := []byte("to")
blockhash := testBlock.Header.Hash()
hexKey := common.BytesToHex(testKey)
keys := []string{hexKey}

changes, err := s.tryQueryStorage(blockhash, keys...)
require.Error(t, err)
require.Nil(t, changes)
}

func TestQueryStorate_WhenBlocksHasData(t *testing.T) {
keys := []string{
common.BytesToHex([]byte("transfer.to")),
common.BytesToHex([]byte("transfer.from")),
common.BytesToHex([]byte("transfer.value")),
}

s := NewTestService(t, nil)

firstKey, firstValue := []byte("transfer.to"), []byte("some-address-herer")
firstBlock := createNewBlockAndStoreDataAtBlock(
t, s, firstKey, firstValue, s.blockState.GenesisHash(), 1,
)

secondKey, secondValue := []byte("transfer.from"), []byte("another-address-here")
secondBlock := createNewBlockAndStoreDataAtBlock(
t, s, secondKey, secondValue, firstBlock.Header.Hash(), 2,
)

thirdKey, thirdValue := []byte("transfer.value"), []byte("value-gigamegablaster")
thirdBlock := createNewBlockAndStoreDataAtBlock(
t, s, thirdKey, thirdValue, secondBlock.Header.Hash(), 3,
)

from := firstBlock.Header.Hash()
data, err := s.QueryStorage(from, common.Hash{}, keys...)
require.NoError(t, err)
require.Len(t, data, 3)

require.Equal(t, data[firstBlock.Header.Hash()], QueryKeyValueChanges(
map[string]string{
common.BytesToHex(firstKey): common.BytesToHex(firstValue),
},
))

from = secondBlock.Header.Hash()
to := thirdBlock.Header.Hash()

data, err = s.QueryStorage(from, to, keys...)
require.NoError(t, err)
require.Len(t, data, 2)

require.Equal(t, data[secondBlock.Header.Hash()], QueryKeyValueChanges(
map[string]string{
common.BytesToHex(secondKey): common.BytesToHex(secondValue),
},
))
require.Equal(t, data[thirdBlock.Header.Hash()], QueryKeyValueChanges(
map[string]string{
common.BytesToHex(thirdKey): common.BytesToHex(thirdValue),
},
))
}

func createNewBlockAndStoreDataAtBlock(t *testing.T, s *Service,
key, value []byte, parentHash common.Hash,
number uint) *types.Block {
t.Helper()

storageStateTrie, err := rtstorage.NewTrieState(trie.NewTrie(nil))
storageStateTrie.Set(key, value)
require.NoError(t, err)

digest := newTestDigest(t, 421)
header, err := types.NewHeader(parentHash, storageStateTrie.MustRoot(), common.Hash{}, number, digest)
require.NoError(t, err)

err = s.storageState.StoreTrie(storageStateTrie, header)
require.NoError(t, err)

testBlock := &types.Block{
Header: *header,
Body: *types.NewBody([]types.Extrinsic{}),
}

err = s.blockState.AddBlock(testBlock)
require.NoError(t, err)

return testBlock
}
96 changes: 0 additions & 96 deletions dot/core/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1229,102 +1229,6 @@ func TestServiceGetMetadata(t *testing.T) {
})
}

func TestService_tryQueryStorage(t *testing.T) {
t.Parallel()
execTest := func(t *testing.T, s *Service, block common.Hash, keys []string, exp QueryKeyValueChanges, expErr error) {
res, err := s.tryQueryStorage(block, keys...)
assert.ErrorIs(t, err, expErr)
if expErr != nil {
assert.EqualError(t, err, expErr.Error())
}
assert.Equal(t, exp, res)
}

t.Run("get state root error", func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
mockStorageState := NewMockStorageState(ctrl)
mockStorageState.EXPECT().GetStateRootFromBlock(&common.Hash{}).Return(nil, errDummyErr)
service := &Service{
storageState: mockStorageState,
}
execTest(t, service, common.Hash{}, nil, nil, errDummyErr)
})

t.Run("get storage error", func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
mockStorageState := NewMockStorageState(ctrl)
mockStorageState.EXPECT().GetStateRootFromBlock(&common.Hash{}).Return(&common.Hash{}, nil)
mockStorageState.EXPECT().GetStorage(&common.Hash{}, common.MustHexToBytes("0x01")).Return(nil, errDummyErr)
service := &Service{
storageState: mockStorageState,
}
execTest(t, service, common.Hash{}, []string{"0x01"}, nil, errDummyErr)
})

t.Run("happy path", func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
mockStorageState := NewMockStorageState(ctrl)
mockStorageState.EXPECT().GetStateRootFromBlock(&common.Hash{}).Return(&common.Hash{}, nil)
mockStorageState.EXPECT().GetStorage(&common.Hash{}, common.MustHexToBytes("0x01")).
Return([]byte{1, 2, 3}, nil)
expChanges := make(QueryKeyValueChanges)
expChanges["0x01"] = common.BytesToHex([]byte{1, 2, 3})
service := &Service{
storageState: mockStorageState,
}
execTest(t, service, common.Hash{}, []string{"0x01"}, expChanges, nil)
})
}

func TestService_QueryStorage(t *testing.T) {
t.Parallel()
execTest := func(t *testing.T, s *Service, from common.Hash, to common.Hash,
keys []string, exp map[common.Hash]QueryKeyValueChanges, expErr error) {
res, err := s.QueryStorage(from, to, keys...)
assert.ErrorIs(t, err, expErr)
if expErr != nil {
assert.EqualError(t, err, expErr.Error())
}
assert.Equal(t, exp, res)
}

t.Run("subchain error", func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
mockBlockState := NewMockBlockState(ctrl)
mockBlockState.EXPECT().BestBlockHash().Return(common.Hash{2})
mockBlockState.EXPECT().SubChain(common.Hash{1}, common.Hash{2}).Return(nil, errDummyErr)
service := &Service{
blockState: mockBlockState,
}
execTest(t, service, common.Hash{1}, common.Hash{}, nil, nil, errDummyErr)
})

t.Run("happy path", func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
mockBlockState := NewMockBlockState(ctrl)
mockBlockState.EXPECT().BestBlockHash().Return(common.Hash{2})
mockBlockState.EXPECT().SubChain(common.Hash{1}, common.Hash{2}).Return([]common.Hash{{0x01}}, nil)
mockStorageState := NewMockStorageState(ctrl)
mockStorageState.EXPECT().GetStateRootFromBlock(&common.Hash{0x01}).Return(&common.Hash{}, nil)
mockStorageState.EXPECT().GetStorage(&common.Hash{}, common.MustHexToBytes("0x01")).
Return([]byte{1, 2, 3}, nil)
expChanges := make(QueryKeyValueChanges)
expChanges["0x01"] = common.BytesToHex([]byte{1, 2, 3})
expQueries := make(map[common.Hash]QueryKeyValueChanges)
expQueries[common.Hash{0x01}] = expChanges
service := &Service{
blockState: mockBlockState,
storageState: mockStorageState,
}
execTest(t, service, common.Hash{1}, common.Hash{}, []string{"0x01"}, expQueries, nil)
})
}

func TestService_GetReadProofAt(t *testing.T) {
t.Parallel()
execTest := func(t *testing.T, s *Service, block common.Hash, keys [][]byte,
Expand Down
3 changes: 2 additions & 1 deletion dot/rpc/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ func (h *HTTPServer) RegisterModules(mods []string) {
case "grandpa":
srvc = modules.NewGrandpaModule(h.serverConfig.BlockAPI, h.serverConfig.BlockFinalityAPI)
case "state":
srvc = modules.NewStateModule(h.serverConfig.NetworkAPI, h.serverConfig.StorageAPI, h.serverConfig.CoreAPI)
srvc = modules.NewStateModule(h.serverConfig.NetworkAPI, h.serverConfig.StorageAPI,
h.serverConfig.CoreAPI, h.serverConfig.BlockAPI)
case "rpc":
srvc = modules.NewRPCModule(h.serverConfig.RPCAPI)
case "dev":
Expand Down
Loading

0 comments on commit 1ec0d47

Please sign in to comment.