Skip to content

Commit d5749ac

Browse files
Merge branch 'development' into eclesio/rpc-add-reserved-peer
2 parents 75e09d3 + d30c7ed commit d5749ac

File tree

10 files changed

+370
-11
lines changed

10 files changed

+370
-11
lines changed

.github/workflows/tests.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ jobs:
132132
name: Run stable tests
133133
run: |
134134
docker run chainsafe/gossamer:test sh -c "make it-stable"
135+
135136
docker-rpc-tests:
136137
runs-on: ubuntu-latest
137138
steps:
@@ -155,6 +156,7 @@ jobs:
155156
name: Run rpc tests
156157
run: |
157158
docker run chainsafe/gossamer:test sh -c "make it-rpc"
159+
158160
docker-stress-tests:
159161
runs-on: ubuntu-latest
160162
steps:
@@ -178,6 +180,7 @@ jobs:
178180
name: Run stress
179181
run: |
180182
docker run chainsafe/gossamer:test sh -c "make it-stress"
183+
181184
docker-grandpa-tests:
182185
runs-on: ubuntu-latest
183186
steps:

dot/core/interface.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ type StorageState interface {
6060
TrieState(root *common.Hash) (*rtstorage.TrieState, error)
6161
StoreTrie(*rtstorage.TrieState, *types.Header) error
6262
GetStateRootFromBlock(bhash *common.Hash) (*common.Hash, error)
63+
GetStorage(root *common.Hash, key []byte) ([]byte, error)
6364
}
6465

6566
// TransactionState is the interface for transaction state methods

dot/core/service.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ var (
4040
logger log.Logger = log.New("pkg", "core")
4141
)
4242

43+
// QueryKeyValueChanges represents the key-value data inside a block storage
44+
type QueryKeyValueChanges map[string]string
45+
4346
// Service is an overhead layer that allows communication between the runtime,
4447
// BABE session, and network service. It deals with the validation of transactions
4548
// and blocks by calling their respective validation functions in the runtime.
@@ -560,3 +563,58 @@ func (s *Service) GetMetadata(bhash *common.Hash) ([]byte, error) {
560563
rt.SetContextStorage(ts)
561564
return rt.Metadata()
562565
}
566+
567+
// QueryStorage returns the key-value data by block based on `keys` params
568+
// on every block starting `from` until `to` block, if `to` is not nil
569+
func (s *Service) QueryStorage(from, to common.Hash, keys ...string) (map[common.Hash]QueryKeyValueChanges, error) {
570+
if to == common.EmptyHash {
571+
to = s.blockState.BestBlockHash()
572+
}
573+
574+
blocksToQuery, err := s.blockState.SubChain(from, to)
575+
if err != nil {
576+
return nil, err
577+
}
578+
579+
queries := make(map[common.Hash]QueryKeyValueChanges)
580+
581+
for _, hash := range blocksToQuery {
582+
changes, err := s.tryQueryStorage(hash, keys...)
583+
if err != nil {
584+
return nil, err
585+
}
586+
587+
queries[hash] = changes
588+
}
589+
590+
return queries, nil
591+
}
592+
593+
// tryQueryStorage will try to get all the `keys` inside the block's current state
594+
func (s *Service) tryQueryStorage(block common.Hash, keys ...string) (QueryKeyValueChanges, error) {
595+
stateRootHash, err := s.storageState.GetStateRootFromBlock(&block)
596+
if err != nil {
597+
return nil, err
598+
}
599+
600+
changes := make(QueryKeyValueChanges)
601+
for _, k := range keys {
602+
keyBytes, err := common.HexToBytes(k)
603+
if err != nil {
604+
return nil, err
605+
}
606+
607+
storedData, err := s.storageState.GetStorage(stateRootHash, keyBytes)
608+
if err != nil {
609+
return nil, err
610+
}
611+
612+
if storedData == nil {
613+
continue
614+
}
615+
616+
changes[k] = common.BytesToHex(storedData)
617+
}
618+
619+
return changes, nil
620+
}

dot/core/service_test.go

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ import (
3333
"github.com/ChainSafe/gossamer/lib/keystore"
3434
"github.com/ChainSafe/gossamer/lib/runtime"
3535
"github.com/ChainSafe/gossamer/lib/runtime/extrinsic"
36+
"github.com/ChainSafe/gossamer/lib/runtime/storage"
3637
"github.com/ChainSafe/gossamer/lib/runtime/wasmer"
3738
"github.com/ChainSafe/gossamer/lib/transaction"
39+
"github.com/ChainSafe/gossamer/lib/trie"
3840
"github.com/ChainSafe/gossamer/lib/utils"
3941
log "github.com/ChainSafe/log15"
4042
"github.com/stretchr/testify/mock"
@@ -619,6 +621,173 @@ func TestService_HandleRuntimeChangesAfterCodeSubstitutes(t *testing.T) {
619621
require.NotEqualf(t, codeHashBefore, rt.GetCodeHash(), "expected different code hash after runtime update") // codeHash should change after runtime change
620622
}
621623

624+
func TestTryQueryStore_WhenThereIsDataToRetrieve(t *testing.T) {
625+
s := NewTestService(t, nil)
626+
storageStateTrie, err := storage.NewTrieState(trie.NewTrie(nil))
627+
628+
testKey, testValue := []byte("to"), []byte("0x1723712318238AB12312")
629+
storageStateTrie.Set(testKey, testValue)
630+
require.NoError(t, err)
631+
632+
header, err := types.NewHeader(s.blockState.GenesisHash(), storageStateTrie.MustRoot(),
633+
common.Hash{}, big.NewInt(1), nil)
634+
require.NoError(t, err)
635+
636+
err = s.storageState.StoreTrie(storageStateTrie, header)
637+
require.NoError(t, err)
638+
639+
testBlock := &types.Block{
640+
Header: header,
641+
Body: types.NewBody([]byte{}),
642+
}
643+
644+
err = s.blockState.AddBlock(testBlock)
645+
require.NoError(t, err)
646+
647+
blockhash := testBlock.Header.Hash()
648+
hexKey := common.BytesToHex(testKey)
649+
keys := []string{hexKey}
650+
651+
changes, err := s.tryQueryStorage(blockhash, keys...)
652+
require.NoError(t, err)
653+
654+
require.Equal(t, changes[hexKey], common.BytesToHex(testValue))
655+
}
656+
657+
func TestTryQueryStore_WhenDoesNotHaveDataToRetrieve(t *testing.T) {
658+
s := NewTestService(t, nil)
659+
storageStateTrie, err := storage.NewTrieState(trie.NewTrie(nil))
660+
require.NoError(t, err)
661+
662+
header, err := types.NewHeader(s.blockState.GenesisHash(), storageStateTrie.MustRoot(),
663+
common.Hash{}, big.NewInt(1), nil)
664+
require.NoError(t, err)
665+
666+
err = s.storageState.StoreTrie(storageStateTrie, header)
667+
require.NoError(t, err)
668+
669+
testBlock := &types.Block{
670+
Header: header,
671+
Body: types.NewBody([]byte{}),
672+
}
673+
674+
err = s.blockState.AddBlock(testBlock)
675+
require.NoError(t, err)
676+
677+
testKey := []byte("to")
678+
blockhash := testBlock.Header.Hash()
679+
hexKey := common.BytesToHex(testKey)
680+
keys := []string{hexKey}
681+
682+
changes, err := s.tryQueryStorage(blockhash, keys...)
683+
require.NoError(t, err)
684+
685+
require.Empty(t, changes)
686+
}
687+
688+
func TestTryQueryState_WhenDoesNotHaveStateRoot(t *testing.T) {
689+
s := NewTestService(t, nil)
690+
691+
header, err := types.NewHeader(s.blockState.GenesisHash(), common.Hash{}, common.Hash{}, big.NewInt(1), nil)
692+
require.NoError(t, err)
693+
694+
testBlock := &types.Block{
695+
Header: header,
696+
Body: types.NewBody([]byte{}),
697+
}
698+
699+
err = s.blockState.AddBlock(testBlock)
700+
require.NoError(t, err)
701+
702+
testKey := []byte("to")
703+
blockhash := testBlock.Header.Hash()
704+
hexKey := common.BytesToHex(testKey)
705+
keys := []string{hexKey}
706+
707+
changes, err := s.tryQueryStorage(blockhash, keys...)
708+
require.Error(t, err)
709+
require.Nil(t, changes)
710+
}
711+
712+
func TestQueryStorate_WhenBlocksHasData(t *testing.T) {
713+
keys := []string{
714+
common.BytesToHex([]byte("transfer.to")),
715+
common.BytesToHex([]byte("transfer.from")),
716+
common.BytesToHex([]byte("transfer.value")),
717+
}
718+
719+
s := NewTestService(t, nil)
720+
721+
firstKey, firstValue := []byte("transfer.to"), []byte("some-address-herer")
722+
firstBlock := createNewBlockAndStoreDataAtBlock(
723+
t, s, firstKey, firstValue, s.blockState.GenesisHash(), 1,
724+
)
725+
726+
secondKey, secondValue := []byte("transfer.from"), []byte("another-address-here")
727+
secondBlock := createNewBlockAndStoreDataAtBlock(
728+
t, s, secondKey, secondValue, firstBlock.Header.Hash(), 2,
729+
)
730+
731+
thirdKey, thirdValue := []byte("transfer.value"), []byte("value-gigamegablaster")
732+
thirdBlock := createNewBlockAndStoreDataAtBlock(
733+
t, s, thirdKey, thirdValue, secondBlock.Header.Hash(), 3,
734+
)
735+
736+
from := firstBlock.Header.Hash()
737+
data, err := s.QueryStorage(from, common.Hash{}, keys...)
738+
require.NoError(t, err)
739+
require.Len(t, data, 3)
740+
741+
require.Equal(t, data[firstBlock.Header.Hash()], QueryKeyValueChanges(
742+
map[string]string{
743+
common.BytesToHex(firstKey): common.BytesToHex(firstValue),
744+
},
745+
))
746+
747+
from = secondBlock.Header.Hash()
748+
to := thirdBlock.Header.Hash()
749+
750+
data, err = s.QueryStorage(from, to, keys...)
751+
require.NoError(t, err)
752+
require.Len(t, data, 2)
753+
754+
require.Equal(t, data[secondBlock.Header.Hash()], QueryKeyValueChanges(
755+
map[string]string{
756+
common.BytesToHex(secondKey): common.BytesToHex(secondValue),
757+
},
758+
))
759+
require.Equal(t, data[thirdBlock.Header.Hash()], QueryKeyValueChanges(
760+
map[string]string{
761+
common.BytesToHex(thirdKey): common.BytesToHex(thirdValue),
762+
},
763+
))
764+
}
765+
766+
func createNewBlockAndStoreDataAtBlock(t *testing.T, s *Service, key, value []byte, parentHash common.Hash, number int64) *types.Block {
767+
t.Helper()
768+
769+
storageStateTrie, err := storage.NewTrieState(trie.NewTrie(nil))
770+
storageStateTrie.Set(key, value)
771+
require.NoError(t, err)
772+
773+
header, err := types.NewHeader(parentHash, storageStateTrie.MustRoot(),
774+
common.Hash{}, big.NewInt(number), nil)
775+
require.NoError(t, err)
776+
777+
err = s.storageState.StoreTrie(storageStateTrie, header)
778+
require.NoError(t, err)
779+
780+
testBlock := &types.Block{
781+
Header: header,
782+
Body: types.NewBody([]byte{}),
783+
}
784+
785+
err = s.blockState.AddBlock(testBlock)
786+
require.NoError(t, err)
787+
788+
return testBlock
789+
}
790+
622791
func TestDecodeSessionKeys(t *testing.T) {
623792
mockInstance := new(runtimemocks.MockInstance)
624793
mockInstance.On("DecodeSessionKeys", mock.AnythingOfType("[]uint8")).Return([]byte{}, nil).Once()

dot/network/service.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,9 +304,16 @@ func (s *Service) collectNetworkMetrics() {
304304
}
305305

306306
func (s *Service) logPeerCount() {
307+
ticker := time.NewTicker(time.Second * 30)
308+
defer ticker.Stop()
309+
307310
for {
308-
logger.Debug("peer count", "num", s.host.peerCount(), "min", s.cfg.MinPeers, "max", s.cfg.MaxPeers)
309-
time.Sleep(time.Second * 30)
311+
select {
312+
case <-ticker.C:
313+
logger.Debug("peer count", "num", s.host.peerCount(), "min", s.cfg.MinPeers, "max", s.cfg.MaxPeers)
314+
case <-s.ctx.Done():
315+
return
316+
}
310317
}
311318
}
312319

dot/rpc/modules/api.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package modules
33
import (
44
"math/big"
55

6+
"github.com/ChainSafe/gossamer/dot/core"
67
"github.com/ChainSafe/gossamer/dot/state"
78
"github.com/ChainSafe/gossamer/dot/types"
89
"github.com/ChainSafe/gossamer/lib/common"
@@ -79,6 +80,7 @@ type CoreAPI interface {
7980
GetRuntimeVersion(bhash *common.Hash) (runtime.Version, error)
8081
HandleSubmittedExtrinsic(types.Extrinsic) error
8182
GetMetadata(bhash *common.Hash) ([]byte, error)
83+
QueryStorage(from, to common.Hash, keys ...string) (map[common.Hash]core.QueryKeyValueChanges, error)
8284
DecodeSessionKeys(enc []byte) ([]byte, error)
8385
}
8486

dot/rpc/modules/mocks/core_api.go

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)