Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(dot/state) Fix runtime upgrade #1638

Merged
merged 25 commits into from
Jul 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
470b750
fix runtime upgrade
arijitAD Jun 22, 2021
a4a567f
Add runtime in blocktree.
arijitAD Jun 22, 2021
1dbb99c
Remove runtime instance from babe service.
arijitAD Jun 24, 2021
e9d5092
Remove runtime instance from sync service.
arijitAD Jun 24, 2021
5abbe1f
Self review.
arijitAD Jun 28, 2021
bcd8972
Fix chain reorg test.
arijitAD Jun 28, 2021
667d79c
Fix failing test.
arijitAD Jul 5, 2021
73aba8c
Merge remote-tracking branch 'origin/development' into fix-upgrade-ru…
arijitAD Jul 5, 2021
1613cfa
Self review.
arijitAD Jul 5, 2021
a9ed21c
Address comments.
arijitAD Jul 5, 2021
6a14539
Merge branch 'development' into fix-upgrade-runtime
arijitAD Jul 7, 2021
5b4a117
Remove unused functions from interface.
arijitAD Jul 7, 2021
c11dd7c
Fix failing test.
arijitAD Jul 8, 2021
87d981d
Merge branch 'development' into fix-upgrade-runtime
arijitAD Jul 8, 2021
1c5cc7e
fix TestService_HandleSubmittedExtrinsic
noot Jul 8, 2021
109b1e4
update HandleTransactionMessage to set runtime storage before validating
noot Jul 8, 2021
1ba329c
Merge branch 'development' into fix-upgrade-runtime
arijitAD Jul 8, 2021
0249b47
Merge branch 'development' into fix-upgrade-runtime
arijitAD Jul 9, 2021
3e0a35d
Fix failing test.
arijitAD Jul 9, 2021
e8862e2
Merge remote-tracking branch 'origin/development' into fix-upgrade-ru…
arijitAD Jul 12, 2021
131eb76
merge w development
noot Jul 19, 2021
2e2f059
address comment
noot Jul 19, 2021
47377dd
lint
noot Jul 19, 2021
6c8e72c
cleanup
noot Jul 19, 2021
637b69f
Merge branch 'development' into fix-upgrade-runtime
noot Jul 21, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions chain/dev/genesis-spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
"protocolId": "/gossamer/gssmr/0",
"genesis": {
"runtime": {
"Sudo": {
"Key": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY"
},
"Babe": {
"Authorities": [
[
Expand Down
36 changes: 36 additions & 0 deletions chain/dev/genesis.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions dot/core/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/ChainSafe/gossamer/dot/network"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/runtime"
rtstorage "github.com/ChainSafe/gossamer/lib/runtime/storage"
"github.com/ChainSafe/gossamer/lib/transaction"
)
Expand All @@ -48,6 +49,9 @@ type BlockState interface {
HighestCommonAncestor(a, b common.Hash) (common.Hash, error)
SubChain(start, end common.Hash) ([]common.Hash, error)
GetBlockBody(hash common.Hash) (*types.Body, error)
HandleRuntimeChanges(newState *rtstorage.TrieState, in runtime.Instance, bHash common.Hash) error
GetRuntime(*common.Hash) (runtime.Instance, error)
StoreRuntime(common.Hash, runtime.Instance)
}

// StorageState interface for storage state methods
Expand Down
15 changes: 14 additions & 1 deletion dot/core/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,23 @@ func (s *Service) HandleTransactionMessage(msg *network.TransactionMessage) (boo
// get transactions from message extrinsics
txs := msg.Extrinsics
var toPropagate []types.Extrinsic

rt, err := s.blockState.GetRuntime(nil)
if err != nil {
return false, err
}

for _, tx := range txs {
ts, err := s.storageState.TrieState(nil)
if err != nil {
return false, err
}

rt.SetContextStorage(ts)

// validate each transaction
externalExt := types.Extrinsic(append([]byte{byte(types.TxnExternal)}, tx...))
val, err := s.rt.ValidateTransaction(externalExt)
val, err := rt.ValidateTransaction(externalExt)
if err != nil {
logger.Debug("failed to validate transaction", "err", err)
continue
Expand Down
17 changes: 13 additions & 4 deletions dot/core/messages_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
. "github.com/ChainSafe/gossamer/dot/core/mocks" // nolint
"github.com/ChainSafe/gossamer/dot/network"
"github.com/ChainSafe/gossamer/dot/state"
"github.com/ChainSafe/gossamer/dot/sync"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/crypto/sr25519"
Expand Down Expand Up @@ -140,14 +141,22 @@ func TestService_HandleTransactionMessage(t *testing.T) {

s := NewTestService(t, cfg)
genHash := s.blockState.GenesisHash()
header, err := types.NewHeader(genHash, common.Hash{}, common.Hash{}, big.NewInt(1), types.NewEmptyDigest())
genHeader, err := s.blockState.BestBlockHeader()
require.NoError(t, err)

// initialise block header
err = s.rt.InitializeBlock(header)
rt, err := s.blockState.GetRuntime(nil)
require.NoError(t, err)

extBytes := createExtrinsic(t, s.rt, genHash, 0)
ts, err := s.storageState.TrieState(nil)
require.NoError(t, err)
rt.SetContextStorage(ts)

block := sync.BuildBlock(t, rt, genHeader, nil)

err = s.handleBlock(block, ts)
require.NoError(t, err)

extBytes := createExtrinsic(t, rt, genHash, 0)
msg := &network.TransactionMessage{Extrinsics: []types.Extrinsic{extBytes}}
b, err := s.HandleTransactionMessage(msg)
require.NoError(t, err)
Expand Down
135 changes: 52 additions & 83 deletions dot/core/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@
package core

import (
"bytes"
"context"
"fmt"
"math/big"
"os"
"sync"
Expand Down Expand Up @@ -59,10 +57,6 @@ type Service struct {
net Network
digestHandler DigestHandler

// Current runtime and hash of the current runtime code
rt runtime.Instance
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should Runtime also be removed from Config struct since the service is no longer storing it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is using this runtime. I have created another issue to remove the runtime. #1674

codeHash common.Hash

// map of code substitutions keyed by block hash
codeSubstitute map[common.Hash]string
codeSubstitutedState CodeSubstitutedState
Expand Down Expand Up @@ -103,10 +97,6 @@ func NewService(cfg *Config) (*Service, error) {
return nil, ErrNilStorageState
}

if cfg.Runtime == nil {
return nil, ErrNilRuntime
}

if cfg.Network == nil {
return nil, ErrNilNetwork
}
Expand All @@ -123,24 +113,12 @@ func NewService(cfg *Config) (*Service, error) {
h = log.CallerFileHandler(h)
logger.SetHandler(log.LvlFilterHandler(cfg.LogLvl, h))

sr, err := cfg.BlockState.BestBlockStateRoot()
if err != nil {
return nil, err
}

codeHash, err := cfg.StorageState.LoadCodeHash(&sr)
if err != nil {
return nil, err
}

blockAddCh := make(chan *types.Block, 256)

ctx, cancel := context.WithCancel(context.Background())
srv := &Service{
ctx: ctx,
cancel: cancel,
rt: cfg.Runtime,
codeHash: codeHash,
keys: cfg.Keystore,
blockState: cfg.BlockState,
epochState: cfg.EpochState,
Expand Down Expand Up @@ -221,7 +199,7 @@ func (s *Service) handleBlock(block *types.Block, state *rtstorage.TrieState) er
}

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

rt, err := s.blockState.GetRuntime(&block.Header.ParentHash)
if err != nil {
return err
}

// check for runtime changes
if err := s.handleRuntimeChanges(state); err != nil {
if err := s.blockState.HandleRuntimeChanges(state, rt, block.Header.Hash()); err != nil {
logger.Crit("failed to update runtime code", "error", err)
return err
}
Expand Down Expand Up @@ -267,57 +250,6 @@ func (s *Service) handleBlock(block *types.Block, state *rtstorage.TrieState) er
return nil
}

func (s *Service) handleRuntimeChanges(newState *rtstorage.TrieState) error {
currCodeHash, err := newState.LoadCodeHash()
if err != nil {
return err
}

if bytes.Equal(s.codeHash[:], currCodeHash[:]) {
return nil
}

logger.Info("🔄 detected runtime code change, upgrading...", "block", s.blockState.BestBlockHash(), "previous code hash", s.codeHash, "new code hash", currCodeHash)
code := newState.LoadCode()
if len(code) == 0 {
return ErrEmptyRuntimeCode
}

codeSubBlockHash := s.codeSubstitutedState.LoadCodeSubstitutedBlockHash()

if !codeSubBlockHash.Equal(common.Hash{}) {
// don't do runtime change if using code substitution and runtime change spec version are equal
// (do a runtime change if code substituted and runtime spec versions are different, or code not substituted)
newVersion, err := s.rt.CheckRuntimeVersion(code) //nolint
if err != nil {
return err
}

previousVersion, _ := s.rt.Version()
if previousVersion.SpecVersion() == newVersion.SpecVersion() {
return nil
}

logger.Info("🔄 detected runtime code change, upgrading...", "block", s.blockState.BestBlockHash(),
"previous code hash", s.codeHash, "new code hash", currCodeHash,
"previous spec version", previousVersion.SpecVersion(), "new spec version", newVersion.SpecVersion())
}

err = s.rt.UpdateRuntimeCode(code)
if err != nil {
return err
}

s.codeHash = currCodeHash

err = s.codeSubstitutedState.StoreCodeSubstitutedBlockHash(common.Hash{})
if err != nil {
return fmt.Errorf("failed to update code substituted block hash: %w", err)
}

return nil
}

func (s *Service) handleCodeSubstitution(hash common.Hash) error {
value := s.codeSubstitute[hash]
if value == "" {
Expand All @@ -330,7 +262,12 @@ func (s *Service) handleCodeSubstitution(hash common.Hash) error {
return ErrEmptyRuntimeCode
}

err := s.rt.UpdateRuntimeCode(code)
rt, err := s.blockState.GetRuntime(&hash)
if err != nil {
return err
}

err = rt.UpdateRuntimeCode(code)
if err != nil {
return err
}
Expand Down Expand Up @@ -415,6 +352,12 @@ func (s *Service) handleChainReorg(prev, curr common.Hash) error {
subchain = subchain[1:]
}

// Check transaction validation on the best block.
rt, err := s.blockState.GetRuntime(nil)
if err != nil {
return err
}

// for each block in the previous chain, re-add its extrinsics back into the pool
for _, hash := range subchain {
body, err := s.blockState.GetBlockBody(hash)
Expand Down Expand Up @@ -448,7 +391,7 @@ func (s *Service) handleChainReorg(prev, curr common.Hash) error {
}

externalExt := types.Extrinsic(append([]byte{byte(types.TxnExternal)}, encExt...))
txv, err := s.rt.ValidateTransaction(externalExt)
txv, err := rt.ValidateTransaction(externalExt)
if err != nil {
logger.Debug("failed to validate transaction", "error", err, "extrinsic", ext)
continue
Expand Down Expand Up @@ -519,6 +462,7 @@ func (s *Service) HasKey(pubKeyStr, keyType string) (bool, error) {
// GetRuntimeVersion gets the current RuntimeVersion
func (s *Service) GetRuntimeVersion(bhash *common.Hash) (runtime.Version, error) {
var stateRootHash *common.Hash

// If block hash is not nil then fetch the state root corresponding to the block.
if bhash != nil {
var err error
Expand All @@ -533,16 +477,36 @@ func (s *Service) GetRuntimeVersion(bhash *common.Hash) (runtime.Version, error)
return nil, err
}

s.rt.SetContextStorage(ts)
return s.rt.Version()
rt, err := s.blockState.GetRuntime(bhash)
if err != nil {
return nil, err
}

rt.SetContextStorage(ts)
return rt.Version()
}

// HandleSubmittedExtrinsic is used to send a Transaction message containing a Extrinsic @ext
func (s *Service) HandleSubmittedExtrinsic(ext types.Extrinsic) error {
if s.net == nil {
return nil
}

ts, err := s.storageState.TrieState(nil)
if err != nil {
return err
}

rt, err := s.blockState.GetRuntime(nil)
if err != nil {
logger.Crit("failed to get runtime")
return err
}

rt.SetContextStorage(ts)
// the transaction source is External
externalExt := types.Extrinsic(append([]byte{byte(types.TxnExternal)}, ext...))

txv, err := s.rt.ValidateTransaction(externalExt)
txv, err := rt.ValidateTransaction(externalExt)
if err != nil {
return err
}
Expand Down Expand Up @@ -576,6 +540,11 @@ func (s *Service) GetMetadata(bhash *common.Hash) ([]byte, error) {
return nil, err
}

s.rt.SetContextStorage(ts)
return s.rt.Metadata()
rt, err := s.blockState.GetRuntime(bhash)
if err != nil {
return nil, err
}

rt.SetContextStorage(ts)
return rt.Metadata()
}
Loading