Skip to content

Commit

Permalink
feat(rpc): Implement state_getReadProof rpc call (ChainSafe#1768)
Browse files Browse the repository at this point in the history
* feat: implement chain subscribe all heads

* chore: add unit tests to subscribe all heads

* chore: fix imports

* feat: implement state_getReadProof rpc call

* chore: fix unused import

* chore: fix lint warnings

* wip: proof generation algotithm

* wip: append not working

* wip: fixing functions

* wip: remove unused code

* feat: proof generation working

* chore: add multiple keys proof generation

* chore: add unit tests

* chore: add unit test

* chore: fix lint warnings

* chore: update GenerateTrieProof from block to storage state

* chore: remove repeated code

* chore: resolve fix lint

* chore: update tests

* chore: simplify to sliced value itself

* chore: deepsource ignore auto generated files

* chore: remove unused refs

* chore: addressing deepsource warngins

* chore: addressing deepsource wars

* chore: remove DS_Store

* chore: fix typo

* chore: use IsEmpty as record method

* chore: update comment export

* chore: resolve errors

* chore: resolve exported

* chore: resolve test to dont exported

* chore: resolve interface mocks

* chore: remove unused code

* chore: resolve unues funcs

* chore: resolve failing unit tests

* chore: update dot/core/services.go unit test coverage

* chore: remove skip from test

* chore: remove comment
  • Loading branch information
EclesioMeloJunior committed Sep 30, 2021
1 parent 33059ad commit 865f80f
Show file tree
Hide file tree
Showing 20 changed files with 622 additions and 347 deletions.
2 changes: 2 additions & 0 deletions dot/core/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type BlockState interface {
AddBlock(*types.Block) error
GetAllBlocksAtDepth(hash common.Hash) []common.Hash
GetBlockByHash(common.Hash) (*types.Block, error)
GetBlockStateRoot(bhash common.Hash) (common.Hash, error)
GenesisHash() common.Hash
GetSlotForBlock(common.Hash) (uint64, error)
GetFinalisedHeader(uint64, uint64) (*types.Header, error)
Expand All @@ -62,6 +63,7 @@ type StorageState interface {
StoreTrie(*rtstorage.TrieState, *types.Header) error
GetStateRootFromBlock(bhash *common.Hash) (*common.Hash, error)
GetStorage(root *common.Hash, key []byte) ([]byte, error)
GenerateTrieProof(stateRoot common.Hash, keys [][]byte) ([][]byte, error)
sync.Locker
}

Expand Down
25 changes: 24 additions & 1 deletion dot/core/mocks/block_state.go

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

180 changes: 180 additions & 0 deletions dot/core/mocks/storage_state.go

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

20 changes: 20 additions & 0 deletions dot/core/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -627,3 +627,23 @@ func (s *Service) tryQueryStorage(block common.Hash, keys ...string) (QueryKeyVa

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) (common.Hash, [][]byte, error) {
if common.EmptyHash.Equal(block) {
block = s.blockState.BestBlockHash()
}

stateRoot, err := s.blockState.GetBlockStateRoot(block)
if err != nil {
return common.EmptyHash, nil, err
}

proofForKeys, err := s.storageState.GenerateTrieProof(stateRoot, keys)
if err != nil {
return common.EmptyHash, nil, err
}

return block, proofForKeys, nil
}
76 changes: 74 additions & 2 deletions dot/core/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import (
"time"

"github.com/ChainSafe/gossamer/dot/core/mocks"
coremocks "github.com/ChainSafe/gossamer/dot/core/mocks"
"github.com/ChainSafe/gossamer/dot/network"
"github.com/ChainSafe/gossamer/dot/state"
"github.com/ChainSafe/gossamer/dot/sync"
Expand Down Expand Up @@ -107,7 +106,7 @@ func TestStartService(t *testing.T) {
}

func TestAnnounceBlock(t *testing.T) {
net := new(coremocks.MockNetwork)
net := new(mocks.MockNetwork)
cfg := &Config{
Network: net,
}
Expand Down Expand Up @@ -829,3 +828,76 @@ func TestDecodeSessionKeys_WhenGetRuntimeReturnError(t *testing.T) {
require.Error(t, err, "problems")
require.Nil(t, b)
}

func TestGetReadProofAt(t *testing.T) {
keysToProof := [][]byte{[]byte("first_key"), []byte("another_key")}
mockedProofs := [][]byte{[]byte("proof01"), []byte("proof02")}

t.Run("When Has Block Is Empty", func(t *testing.T) {
mockedStateRootHash := common.NewHash([]byte("state root hash"))
expectedBlockHash := common.NewHash([]byte("expected block hash"))

mockBlockState := new(mocks.MockBlockState)
mockBlockState.On("BestBlockHash").Return(expectedBlockHash)
mockBlockState.On("GetBlockStateRoot", expectedBlockHash).
Return(mockedStateRootHash, nil)

mockStorageStage := new(mocks.MockStorageState)
mockStorageStage.On("GenerateTrieProof", mockedStateRootHash, keysToProof).
Return(mockedProofs, nil)

s := &Service{
blockState: mockBlockState,
storageState: mockStorageStage,
}

b, p, err := s.GetReadProofAt(common.EmptyHash, keysToProof)
require.NoError(t, err)
require.Equal(t, p, mockedProofs)
require.Equal(t, expectedBlockHash, b)

mockBlockState.AssertCalled(t, "BestBlockHash")
mockBlockState.AssertCalled(t, "GetBlockStateRoot", expectedBlockHash)
mockStorageStage.AssertCalled(t, "GenerateTrieProof", mockedStateRootHash, keysToProof)
})

t.Run("When GetStateRoot fails", func(t *testing.T) {
mockedBlockHash := common.NewHash([]byte("fake block hash"))

mockBlockState := new(mocks.MockBlockState)
mockBlockState.On("GetBlockStateRoot", mockedBlockHash).
Return(common.EmptyHash, errors.New("problems while getting state root"))

s := &Service{
blockState: mockBlockState,
}

b, p, err := s.GetReadProofAt(mockedBlockHash, keysToProof)
require.Equal(t, common.EmptyHash, b)
require.Nil(t, p)
require.Error(t, err)
})

t.Run("When GenerateTrieProof fails", func(t *testing.T) {
mockedBlockHash := common.NewHash([]byte("fake block hash"))
mockedStateRootHash := common.NewHash([]byte("state root hash"))

mockBlockState := new(mocks.MockBlockState)
mockBlockState.On("GetBlockStateRoot", mockedBlockHash).
Return(mockedStateRootHash, nil)

mockStorageStage := new(mocks.MockStorageState)
mockStorageStage.On("GenerateTrieProof", mockedStateRootHash, keysToProof).
Return(nil, errors.New("problems to generate trie proof"))

s := &Service{
blockState: mockBlockState,
storageState: mockStorageStage,
}

b, p, err := s.GetReadProofAt(mockedBlockHash, keysToProof)
require.Equal(t, common.EmptyHash, b)
require.Nil(t, p)
require.Error(t, err)
})
}
1 change: 1 addition & 0 deletions dot/rpc/modules/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ type CoreAPI interface {
GetMetadata(bhash *common.Hash) ([]byte, error)
QueryStorage(from, to common.Hash, keys ...string) (map[common.Hash]core.QueryKeyValueChanges, error)
DecodeSessionKeys(enc []byte) ([]byte, error)
GetReadProofAt(block common.Hash, keys [][]byte) (common.Hash, [][]byte, error)
}

// RPCAPI is the interface for methods related to RPC service
Expand Down
32 changes: 32 additions & 0 deletions dot/rpc/modules/mocks/core_api.go

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

Loading

0 comments on commit 865f80f

Please sign in to comment.