Skip to content

Commit b31bba5

Browse files
arijitADnoot
authored andcommitted
fix(dot/state): track runtime per-block, fix runtime upgrades differing between forks (ChainSafe#1638)
* fix runtime upgrade * Add runtime in blocktree. * Remove runtime instance from babe service. * Remove runtime instance from sync service. * Self review. * Fix chain reorg test. * Fix failing test. * Self review. * Address comments. * Remove unused functions from interface. * Fix failing test. * fix TestService_HandleSubmittedExtrinsic * update HandleTransactionMessage to set runtime storage before validating * Fix failing test. * address comment * lint * cleanup Co-authored-by: noot <[email protected]> Co-authored-by: noot <[email protected]>
1 parent 0639451 commit b31bba5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+961
-468
lines changed

chain/dev/genesis-spec.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
"protocolId": "/gossamer/gssmr/0",
88
"genesis": {
99
"runtime": {
10+
"Sudo": {
11+
"Key": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
12+
},
1013
"Babe": {
1114
"Authorities": [
1215
[

chain/dev/genesis.json

Lines changed: 36 additions & 0 deletions
Large diffs are not rendered by default.

dot/core/interface.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/ChainSafe/gossamer/dot/network"
2323
"github.com/ChainSafe/gossamer/dot/types"
2424
"github.com/ChainSafe/gossamer/lib/common"
25+
"github.com/ChainSafe/gossamer/lib/runtime"
2526
rtstorage "github.com/ChainSafe/gossamer/lib/runtime/storage"
2627
"github.com/ChainSafe/gossamer/lib/transaction"
2728
)
@@ -48,6 +49,9 @@ type BlockState interface {
4849
HighestCommonAncestor(a, b common.Hash) (common.Hash, error)
4950
SubChain(start, end common.Hash) ([]common.Hash, error)
5051
GetBlockBody(hash common.Hash) (*types.Body, error)
52+
HandleRuntimeChanges(newState *rtstorage.TrieState, in runtime.Instance, bHash common.Hash) error
53+
GetRuntime(*common.Hash) (runtime.Instance, error)
54+
StoreRuntime(common.Hash, runtime.Instance)
5155
}
5256

5357
// StorageState interface for storage state methods

dot/core/messages.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,23 @@ func (s *Service) HandleTransactionMessage(msg *network.TransactionMessage) (boo
3131
// get transactions from message extrinsics
3232
txs := msg.Extrinsics
3333
var toPropagate []types.Extrinsic
34+
35+
rt, err := s.blockState.GetRuntime(nil)
36+
if err != nil {
37+
return false, err
38+
}
39+
3440
for _, tx := range txs {
41+
ts, err := s.storageState.TrieState(nil)
42+
if err != nil {
43+
return false, err
44+
}
45+
46+
rt.SetContextStorage(ts)
47+
3548
// validate each transaction
3649
externalExt := types.Extrinsic(append([]byte{byte(types.TxnExternal)}, tx...))
37-
val, err := s.rt.ValidateTransaction(externalExt)
50+
val, err := rt.ValidateTransaction(externalExt)
3851
if err != nil {
3952
logger.Debug("failed to validate transaction", "err", err)
4053
continue

dot/core/messages_test.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
. "github.com/ChainSafe/gossamer/dot/core/mocks" // nolint
2525
"github.com/ChainSafe/gossamer/dot/network"
2626
"github.com/ChainSafe/gossamer/dot/state"
27+
"github.com/ChainSafe/gossamer/dot/sync"
2728
"github.com/ChainSafe/gossamer/dot/types"
2829
"github.com/ChainSafe/gossamer/lib/common"
2930
"github.com/ChainSafe/gossamer/lib/crypto/sr25519"
@@ -140,14 +141,22 @@ func TestService_HandleTransactionMessage(t *testing.T) {
140141

141142
s := NewTestService(t, cfg)
142143
genHash := s.blockState.GenesisHash()
143-
header, err := types.NewHeader(genHash, common.Hash{}, common.Hash{}, big.NewInt(1), types.NewEmptyDigest())
144+
genHeader, err := s.blockState.BestBlockHeader()
144145
require.NoError(t, err)
145146

146-
// initialise block header
147-
err = s.rt.InitializeBlock(header)
147+
rt, err := s.blockState.GetRuntime(nil)
148148
require.NoError(t, err)
149149

150-
extBytes := createExtrinsic(t, s.rt, genHash, 0)
150+
ts, err := s.storageState.TrieState(nil)
151+
require.NoError(t, err)
152+
rt.SetContextStorage(ts)
153+
154+
block := sync.BuildBlock(t, rt, genHeader, nil)
155+
156+
err = s.handleBlock(block, ts)
157+
require.NoError(t, err)
158+
159+
extBytes := createExtrinsic(t, rt, genHash, 0)
151160
msg := &network.TransactionMessage{Extrinsics: []types.Extrinsic{extBytes}}
152161
b, err := s.HandleTransactionMessage(msg)
153162
require.NoError(t, err)

dot/core/service.go

Lines changed: 52 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@
1616
package core
1717

1818
import (
19-
"bytes"
2019
"context"
21-
"fmt"
2220
"math/big"
2321
"os"
2422
"sync"
@@ -59,10 +57,6 @@ type Service struct {
5957
net Network
6058
digestHandler DigestHandler
6159

62-
// Current runtime and hash of the current runtime code
63-
rt runtime.Instance
64-
codeHash common.Hash
65-
6660
// map of code substitutions keyed by block hash
6761
codeSubstitute map[common.Hash]string
6862
codeSubstitutedState CodeSubstitutedState
@@ -103,10 +97,6 @@ func NewService(cfg *Config) (*Service, error) {
10397
return nil, ErrNilStorageState
10498
}
10599

106-
if cfg.Runtime == nil {
107-
return nil, ErrNilRuntime
108-
}
109-
110100
if cfg.Network == nil {
111101
return nil, ErrNilNetwork
112102
}
@@ -123,24 +113,12 @@ func NewService(cfg *Config) (*Service, error) {
123113
h = log.CallerFileHandler(h)
124114
logger.SetHandler(log.LvlFilterHandler(cfg.LogLvl, h))
125115

126-
sr, err := cfg.BlockState.BestBlockStateRoot()
127-
if err != nil {
128-
return nil, err
129-
}
130-
131-
codeHash, err := cfg.StorageState.LoadCodeHash(&sr)
132-
if err != nil {
133-
return nil, err
134-
}
135-
136116
blockAddCh := make(chan *types.Block, 256)
137117

138118
ctx, cancel := context.WithCancel(context.Background())
139119
srv := &Service{
140120
ctx: ctx,
141121
cancel: cancel,
142-
rt: cfg.Runtime,
143-
codeHash: codeHash,
144122
keys: cfg.Keystore,
145123
blockState: cfg.BlockState,
146124
epochState: cfg.EpochState,
@@ -221,7 +199,7 @@ func (s *Service) handleBlock(block *types.Block, state *rtstorage.TrieState) er
221199
}
222200

223201
// store block in database
224-
if err := s.blockState.AddBlock(block); err != nil {
202+
if err = s.blockState.AddBlock(block); err != nil {
225203
if err == blocktree.ErrParentNotFound && block.Header.Number.Cmp(big.NewInt(0)) != 0 {
226204
return err
227205
} else if err == blocktree.ErrBlockExists || block.Header.Number.Cmp(big.NewInt(0)) == 0 {
@@ -236,8 +214,13 @@ func (s *Service) handleBlock(block *types.Block, state *rtstorage.TrieState) er
236214
// handle consensus digests
237215
s.digestHandler.HandleDigests(block.Header)
238216

217+
rt, err := s.blockState.GetRuntime(&block.Header.ParentHash)
218+
if err != nil {
219+
return err
220+
}
221+
239222
// check for runtime changes
240-
if err := s.handleRuntimeChanges(state); err != nil {
223+
if err := s.blockState.HandleRuntimeChanges(state, rt, block.Header.Hash()); err != nil {
241224
logger.Crit("failed to update runtime code", "error", err)
242225
return err
243226
}
@@ -267,57 +250,6 @@ func (s *Service) handleBlock(block *types.Block, state *rtstorage.TrieState) er
267250
return nil
268251
}
269252

270-
func (s *Service) handleRuntimeChanges(newState *rtstorage.TrieState) error {
271-
currCodeHash, err := newState.LoadCodeHash()
272-
if err != nil {
273-
return err
274-
}
275-
276-
if bytes.Equal(s.codeHash[:], currCodeHash[:]) {
277-
return nil
278-
}
279-
280-
logger.Info("🔄 detected runtime code change, upgrading...", "block", s.blockState.BestBlockHash(), "previous code hash", s.codeHash, "new code hash", currCodeHash)
281-
code := newState.LoadCode()
282-
if len(code) == 0 {
283-
return ErrEmptyRuntimeCode
284-
}
285-
286-
codeSubBlockHash := s.codeSubstitutedState.LoadCodeSubstitutedBlockHash()
287-
288-
if !codeSubBlockHash.Equal(common.Hash{}) {
289-
// don't do runtime change if using code substitution and runtime change spec version are equal
290-
// (do a runtime change if code substituted and runtime spec versions are different, or code not substituted)
291-
newVersion, err := s.rt.CheckRuntimeVersion(code) //nolint
292-
if err != nil {
293-
return err
294-
}
295-
296-
previousVersion, _ := s.rt.Version()
297-
if previousVersion.SpecVersion() == newVersion.SpecVersion() {
298-
return nil
299-
}
300-
301-
logger.Info("🔄 detected runtime code change, upgrading...", "block", s.blockState.BestBlockHash(),
302-
"previous code hash", s.codeHash, "new code hash", currCodeHash,
303-
"previous spec version", previousVersion.SpecVersion(), "new spec version", newVersion.SpecVersion())
304-
}
305-
306-
err = s.rt.UpdateRuntimeCode(code)
307-
if err != nil {
308-
return err
309-
}
310-
311-
s.codeHash = currCodeHash
312-
313-
err = s.codeSubstitutedState.StoreCodeSubstitutedBlockHash(common.Hash{})
314-
if err != nil {
315-
return fmt.Errorf("failed to update code substituted block hash: %w", err)
316-
}
317-
318-
return nil
319-
}
320-
321253
func (s *Service) handleCodeSubstitution(hash common.Hash) error {
322254
value := s.codeSubstitute[hash]
323255
if value == "" {
@@ -330,7 +262,12 @@ func (s *Service) handleCodeSubstitution(hash common.Hash) error {
330262
return ErrEmptyRuntimeCode
331263
}
332264

333-
err := s.rt.UpdateRuntimeCode(code)
265+
rt, err := s.blockState.GetRuntime(&hash)
266+
if err != nil {
267+
return err
268+
}
269+
270+
err = rt.UpdateRuntimeCode(code)
334271
if err != nil {
335272
return err
336273
}
@@ -415,6 +352,12 @@ func (s *Service) handleChainReorg(prev, curr common.Hash) error {
415352
subchain = subchain[1:]
416353
}
417354

355+
// Check transaction validation on the best block.
356+
rt, err := s.blockState.GetRuntime(nil)
357+
if err != nil {
358+
return err
359+
}
360+
418361
// for each block in the previous chain, re-add its extrinsics back into the pool
419362
for _, hash := range subchain {
420363
body, err := s.blockState.GetBlockBody(hash)
@@ -448,7 +391,7 @@ func (s *Service) handleChainReorg(prev, curr common.Hash) error {
448391
}
449392

450393
externalExt := types.Extrinsic(append([]byte{byte(types.TxnExternal)}, encExt...))
451-
txv, err := s.rt.ValidateTransaction(externalExt)
394+
txv, err := rt.ValidateTransaction(externalExt)
452395
if err != nil {
453396
logger.Debug("failed to validate transaction", "error", err, "extrinsic", ext)
454397
continue
@@ -519,6 +462,7 @@ func (s *Service) HasKey(pubKeyStr, keyType string) (bool, error) {
519462
// GetRuntimeVersion gets the current RuntimeVersion
520463
func (s *Service) GetRuntimeVersion(bhash *common.Hash) (runtime.Version, error) {
521464
var stateRootHash *common.Hash
465+
522466
// If block hash is not nil then fetch the state root corresponding to the block.
523467
if bhash != nil {
524468
var err error
@@ -533,16 +477,36 @@ func (s *Service) GetRuntimeVersion(bhash *common.Hash) (runtime.Version, error)
533477
return nil, err
534478
}
535479

536-
s.rt.SetContextStorage(ts)
537-
return s.rt.Version()
480+
rt, err := s.blockState.GetRuntime(bhash)
481+
if err != nil {
482+
return nil, err
483+
}
484+
485+
rt.SetContextStorage(ts)
486+
return rt.Version()
538487
}
539488

540489
// HandleSubmittedExtrinsic is used to send a Transaction message containing a Extrinsic @ext
541490
func (s *Service) HandleSubmittedExtrinsic(ext types.Extrinsic) error {
491+
if s.net == nil {
492+
return nil
493+
}
494+
495+
ts, err := s.storageState.TrieState(nil)
496+
if err != nil {
497+
return err
498+
}
499+
500+
rt, err := s.blockState.GetRuntime(nil)
501+
if err != nil {
502+
logger.Crit("failed to get runtime")
503+
return err
504+
}
505+
506+
rt.SetContextStorage(ts)
542507
// the transaction source is External
543508
externalExt := types.Extrinsic(append([]byte{byte(types.TxnExternal)}, ext...))
544-
545-
txv, err := s.rt.ValidateTransaction(externalExt)
509+
txv, err := rt.ValidateTransaction(externalExt)
546510
if err != nil {
547511
return err
548512
}
@@ -576,6 +540,11 @@ func (s *Service) GetMetadata(bhash *common.Hash) ([]byte, error) {
576540
return nil, err
577541
}
578542

579-
s.rt.SetContextStorage(ts)
580-
return s.rt.Metadata()
543+
rt, err := s.blockState.GetRuntime(bhash)
544+
if err != nil {
545+
return nil, err
546+
}
547+
548+
rt.SetContextStorage(ts)
549+
return rt.Metadata()
581550
}

0 commit comments

Comments
 (0)