diff --git a/dot/core/errors.go b/dot/core/errors.go
index 5faf6f8b02..cbf554cd2f 100644
--- a/dot/core/errors.go
+++ b/dot/core/errors.go
@@ -21,32 +21,45 @@ import (
"fmt"
)
-// ErrNilBlockState is returned when BlockState is nil
-var ErrNilBlockState = errors.New("cannot have nil BlockState")
+var (
+ // ErrNilBlockState is returned when BlockState is nil
+ ErrNilBlockState = errors.New("cannot have nil BlockState")
-// ErrNilStorageState is returned when StorageState is nil
-var ErrNilStorageState = errors.New("cannot have nil StorageState")
+ // ErrNilStorageState is returned when StorageState is nil
+ ErrNilStorageState = errors.New("cannot have nil StorageState")
-// ErrNilKeystore is returned when keystore is nil
-var ErrNilKeystore = errors.New("cannot have nil keystore")
+ // ErrNilKeystore is returned when keystore is nil
+ ErrNilKeystore = errors.New("cannot have nil keystore")
-// ErrServiceStopped is returned when the service has been stopped
-var ErrServiceStopped = errors.New("service has been stopped")
+ // ErrServiceStopped is returned when the service has been stopped
+ ErrServiceStopped = errors.New("service has been stopped")
-// ErrInvalidBlock is returned when a block cannot be verified
-var ErrInvalidBlock = errors.New("could not verify block")
+ // ErrInvalidBlock is returned when a block cannot be verified
+ ErrInvalidBlock = errors.New("could not verify block")
-// ErrNilVerifier is returned when trying to instantiate a Syncer without a Verifier
-var ErrNilVerifier = errors.New("cannot have nil Verifier")
+ // ErrNilVerifier is returned when trying to instantiate a Syncer without a Verifier
+ ErrNilVerifier = errors.New("cannot have nil Verifier")
-// ErrNilRuntime is returned when trying to instantiate a Service or Syncer without a runtime
-var ErrNilRuntime = errors.New("cannot have nil runtime")
+ // ErrNilRuntime is returned when trying to instantiate a Service or Syncer without a runtime
+ ErrNilRuntime = errors.New("cannot have nil runtime")
-// ErrNilBlockProducer is returned when trying to instantiate a block producing Service without a block producer
-var ErrNilBlockProducer = errors.New("cannot have nil BlockProducer")
+ // ErrNilBlockProducer is returned when trying to instantiate a block producing Service without a block producer
+ ErrNilBlockProducer = errors.New("cannot have nil BlockProducer")
-// ErrNilConsensusMessageHandler is returned when trying to instantiate a Service without a FinalityMessageHandler
-var ErrNilConsensusMessageHandler = errors.New("cannot have nil ErrNilFinalityMessageHandler")
+ // ErrNilConsensusMessageHandler is returned when trying to instantiate a Service without a FinalityMessageHandler
+ ErrNilConsensusMessageHandler = errors.New("cannot have nil ErrNilFinalityMessageHandler")
+
+ // ErrNilNetwork is returned when the Network interface is nil
+ ErrNilNetwork = errors.New("cannot have nil Network")
+
+ // ErrEmptyRuntimeCode is returned when the storage :code is empty
+ ErrEmptyRuntimeCode = errors.New("new :code is empty")
+
+ // ErrNilDigestHandler is returned when the DigestHandler interface is nil
+ ErrNilDigestHandler = errors.New("cannot have nil DigestHandler")
+
+ errNilCodeSubstitutedState = errors.New("cannot have nil CodeSubstitutedStat")
+)
// ErrNilChannel is returned if a channel is nil
func ErrNilChannel(s string) error {
diff --git a/dot/core/interface.go b/dot/core/interface.go
index bab25e3099..48e70ec5ae 100644
--- a/dot/core/interface.go
+++ b/dot/core/interface.go
@@ -22,7 +22,6 @@ import (
"github.com/ChainSafe/gossamer/dot/network"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/common"
- "github.com/ChainSafe/gossamer/lib/grandpa"
rtstorage "github.com/ChainSafe/gossamer/lib/runtime/storage"
"github.com/ChainSafe/gossamer/lib/transaction"
)
@@ -56,6 +55,7 @@ type StorageState interface {
LoadCode(root *common.Hash) ([]byte, error)
LoadCodeHash(root *common.Hash) (common.Hash, error)
TrieState(root *common.Hash) (*rtstorage.TrieState, error)
+ StoreTrie(*rtstorage.TrieState, *types.Header) error
GetStateRootFromBlock(bhash *common.Hash) (*common.Hash, error)
}
@@ -68,17 +68,6 @@ type TransactionState interface {
PendingInPool() []*transaction.ValidTransaction
}
-// BlockProducer is the interface that a block production service must implement
-type BlockProducer interface {
- GetBlockChannel() <-chan types.Block
- SetOnDisabled(authorityIndex uint32)
-}
-
-// Verifier is the interface for the block verifier
-type Verifier interface {
- SetOnDisabled(authorityIndex uint32, block *types.Header) error
-}
-
// Network is the interface for the network service
type Network interface {
SendMessage(network.NotificationsMessage)
@@ -87,17 +76,17 @@ type Network interface {
// EpochState is the interface for state.EpochState
type EpochState interface {
GetEpochForBlock(header *types.Header) (uint64, error)
- SetEpochData(epoch uint64, info *types.EpochData) error
- SetConfigData(epoch uint64, info *types.ConfigData) error
SetCurrentEpoch(epoch uint64) error
GetCurrentEpoch() (uint64, error)
}
-// GrandpaState is the interface for the state.GrandpaState
-type GrandpaState interface {
- SetNextChange(authorities []*grandpa.Voter, number *big.Int) error
- IncrementSetID() error
- SetNextPause(number *big.Int) error
- SetNextResume(number *big.Int) error
- GetCurrentSetID() (uint64, error)
+// CodeSubstitutedState interface to handle storage of code substitute state
+type CodeSubstitutedState interface {
+ LoadCodeSubstitutedBlockHash() common.Hash
+ StoreCodeSubstitutedBlockHash(hash common.Hash) error
+}
+
+// DigestHandler is the interface for the consensus digest handler
+type DigestHandler interface {
+ HandleDigests(header *types.Header)
}
diff --git a/dot/core/messages.go b/dot/core/messages.go
index 8b65d31790..e604da2ff3 100644
--- a/dot/core/messages.go
+++ b/dot/core/messages.go
@@ -26,9 +26,6 @@ import (
// adds valid transactions to the transaction queue of the BABE session
func (s *Service) HandleTransactionMessage(msg *network.TransactionMessage) error {
logger.Debug("received TransactionMessage")
- if !s.isBlockProducer {
- return nil
- }
// get transactions from message extrinsics
txs := msg.Extrinsics
diff --git a/dot/core/messages_test.go b/dot/core/messages_test.go
index cbeaceb990..701c5db2b4 100644
--- a/dot/core/messages_test.go
+++ b/dot/core/messages_test.go
@@ -30,6 +30,7 @@ import (
"github.com/ChainSafe/gossamer/lib/keystore"
"github.com/ChainSafe/gossamer/lib/runtime"
"github.com/ChainSafe/gossamer/lib/scale"
+
"github.com/centrifuge/go-substrate-rpc-client/v2/signature"
ctypes "github.com/centrifuge/go-substrate-rpc-client/v2/types"
"github.com/stretchr/testify/require"
@@ -39,13 +40,9 @@ func TestService_ProcessBlockAnnounceMessage(t *testing.T) {
// TODO: move to sync package
net := new(MockNetwork)
- newBlocks := make(chan types.Block)
-
cfg := &Config{
- Network: net,
- Keystore: keystore.NewGlobalKeystore(),
- NewBlocks: newBlocks,
- IsBlockProducer: false,
+ Network: net,
+ Keystore: keystore.NewGlobalKeystore(),
}
s := NewTestService(t, cfg)
@@ -53,10 +50,11 @@ func TestService_ProcessBlockAnnounceMessage(t *testing.T) {
require.Nil(t, err)
// simulate block sent from BABE session
- newBlock := types.Block{
+ newBlock := &types.Block{
Header: &types.Header{
Number: big.NewInt(1),
ParentHash: s.blockState.BestBlockHash(),
+ Digest: types.Digest{types.NewBabeSecondaryPlainPreDigest(0, 1).ToPreRuntimeDigest()},
},
Body: types.NewBody([]byte{}),
}
@@ -72,10 +70,14 @@ func TestService_ProcessBlockAnnounceMessage(t *testing.T) {
//setup the SendMessage function
net.On("SendMessage", expected)
- newBlocks <- newBlock
- time.Sleep(time.Second * 2)
+ state, err := s.storageState.TrieState(nil)
+ require.NoError(t, err)
+
+ err = s.HandleBlockProduced(newBlock, state)
+ require.NoError(t, err)
+ time.Sleep(time.Second)
net.AssertCalled(t, "SendMessage", expected)
}
@@ -141,8 +143,6 @@ func TestService_HandleTransactionMessage(t *testing.T) {
cfg := &Config{
Keystore: ks,
TransactionState: state.NewTransactionState(),
- IsBlockProducer: true,
- BlockProducer: bp,
}
s := NewTestService(t, cfg)
diff --git a/dot/core/mocks/digest_handler.go b/dot/core/mocks/digest_handler.go
new file mode 100644
index 0000000000..77c8e1a76c
--- /dev/null
+++ b/dot/core/mocks/digest_handler.go
@@ -0,0 +1,18 @@
+// Code generated by mockery v2.8.0. DO NOT EDIT.
+
+package core
+
+import (
+ types "github.com/ChainSafe/gossamer/dot/types"
+ mock "github.com/stretchr/testify/mock"
+)
+
+// MockDigestHandler is an autogenerated mock type for the DigestHandler type
+type MockDigestHandler struct {
+ mock.Mock
+}
+
+// HandleDigests provides a mock function with given fields: header
+func (_m *MockDigestHandler) HandleDigests(header *types.Header) {
+ _m.Called(header)
+}
diff --git a/dot/core/service.go b/dot/core/service.go
index 747944587f..8ee7d402f0 100644
--- a/dot/core/service.go
+++ b/dot/core/service.go
@@ -16,16 +16,21 @@
package core
import (
+ "bytes"
"context"
+ "fmt"
+ "math/big"
"os"
"sync"
"github.com/ChainSafe/gossamer/dot/network"
"github.com/ChainSafe/gossamer/dot/types"
+ "github.com/ChainSafe/gossamer/lib/blocktree"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/crypto"
"github.com/ChainSafe/gossamer/lib/keystore"
"github.com/ChainSafe/gossamer/lib/runtime"
+ rtstorage "github.com/ChainSafe/gossamer/lib/runtime/storage"
"github.com/ChainSafe/gossamer/lib/scale"
"github.com/ChainSafe/gossamer/lib/services"
"github.com/ChainSafe/gossamer/lib/transaction"
@@ -41,43 +46,35 @@ var (
// BABE session, and network service. It deals with the validation of transactions
// and blocks by calling their respective validation functions in the runtime.
type Service struct {
- ctx context.Context
- cancel context.CancelFunc
+ ctx context.Context
+ cancel context.CancelFunc
+ blockAddCh chan *types.Block // for asynchronous block handling
+ sync.Mutex // lock for channel
- // State interfaces
+ // Service interfaces
blockState BlockState
epochState EpochState
storageState StorageState
transactionState TransactionState
+ net Network
+ digestHandler DigestHandler
// Current runtime and hash of the current runtime code
rt runtime.Instance
codeHash common.Hash
- // Block production variables
- blockProducer BlockProducer
- isBlockProducer bool
-
- // Block verification
- verifier Verifier
+ // map of code substitutions keyed by block hash
+ codeSubstitute map[common.Hash]string
+ codeSubstitutedState CodeSubstitutedState
// Keystore
keys *keystore.GlobalKeystore
-
- // Channels and interfaces for inter-process communication
- blkRec <-chan types.Block // receive blocks from BABE session
- net Network
-
- blockAddCh chan *types.Block // receive blocks added to blocktree
- blockAddChID byte
-
- // State variables
- lock *sync.Mutex // channel lock
}
// Config holds the configuration for the core Service.
type Config struct {
- LogLvl log.Lvl
+ LogLvl log.Lvl
+
BlockState BlockState
EpochState EpochState
StorageState StorageState
@@ -85,11 +82,10 @@ type Config struct {
Network Network
Keystore *keystore.GlobalKeystore
Runtime runtime.Instance
- BlockProducer BlockProducer
- IsBlockProducer bool
- Verifier Verifier
+ DigestHandler DigestHandler
- NewBlocks chan types.Block // only used for testing purposes
+ CodeSubstitutes map[common.Hash]string
+ CodeSubstitutedState CodeSubstitutedState
}
// NewService returns a new core service that connects the runtime, BABE
@@ -111,8 +107,16 @@ func NewService(cfg *Config) (*Service, error) {
return nil, ErrNilRuntime
}
- if cfg.IsBlockProducer && cfg.BlockProducer == nil {
- return nil, ErrNilBlockProducer
+ if cfg.Network == nil {
+ return nil, ErrNilNetwork
+ }
+
+ if cfg.DigestHandler == nil {
+ return nil, ErrNilDigestHandler
+ }
+
+ if cfg.CodeSubstitutedState == nil {
+ return nil, errNilCodeSubstitutedState
}
h := log.StreamHandler(os.Stdout, log.TerminalFormat())
@@ -129,38 +133,24 @@ func NewService(cfg *Config) (*Service, error) {
return nil, err
}
- blockAddCh := make(chan *types.Block, 16)
- id, err := cfg.BlockState.RegisterImportedChannel(blockAddCh)
- 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,
- blkRec: cfg.NewBlocks,
- blockState: cfg.BlockState,
- epochState: cfg.EpochState,
- storageState: cfg.StorageState,
- transactionState: cfg.TransactionState,
- net: cfg.Network,
- isBlockProducer: cfg.IsBlockProducer,
- blockProducer: cfg.BlockProducer,
- verifier: cfg.Verifier,
- lock: &sync.Mutex{},
- blockAddCh: blockAddCh,
- blockAddChID: id,
- }
-
- if cfg.NewBlocks != nil {
- srv.blkRec = cfg.NewBlocks
- } else if cfg.IsBlockProducer {
- srv.blkRec = cfg.BlockProducer.GetBlockChannel()
+ ctx: ctx,
+ cancel: cancel,
+ rt: cfg.Runtime,
+ codeHash: codeHash,
+ keys: cfg.Keystore,
+ blockState: cfg.BlockState,
+ epochState: cfg.EpochState,
+ storageState: cfg.StorageState,
+ transactionState: cfg.TransactionState,
+ net: cfg.Network,
+ blockAddCh: blockAddCh,
+ codeSubstitute: cfg.CodeSubstitutes,
+ codeSubstitutedState: cfg.CodeSubstitutedState,
+ digestHandler: cfg.DigestHandler,
}
return srv, nil
@@ -168,29 +158,17 @@ func NewService(cfg *Config) (*Service, error) {
// Start starts the core service
func (s *Service) Start() error {
- // we can ignore the `cancel` function returned by `context.WithCancel` since Stop() cancels the parent context,
- // so all the child contexts should also be canceled. potentially update if there is a better way to do this
-
- // start receiving blocks from BABE session
- go s.receiveBlocks(s.ctx)
-
- // start receiving messages from network service
-
- // start handling imported blocks
- go s.handleBlocks(s.ctx)
-
+ go s.handleBlocksAsync()
return nil
}
// Stop stops the core service
func (s *Service) Stop() error {
- s.lock.Lock()
- defer s.lock.Unlock()
- s.cancel()
+ s.Lock()
+ defer s.Unlock()
- s.blockState.UnregisterImportedChannel(s.blockAddChID)
+ s.cancel()
close(s.blockAddCh)
-
return nil
}
@@ -208,32 +186,161 @@ func (s *Service) StorageRoot() (common.Hash, error) {
return ts.Root()
}
-func (s *Service) handleBlocks(ctx context.Context) {
- for {
- //prev := s.blockState.BestBlockHash()
+// HandleBlockImport handles a block that was imported via the network
+func (s *Service) HandleBlockImport(block *types.Block, state *rtstorage.TrieState) error {
+ return s.handleBlock(block, state)
+}
- select {
- case block := <-s.blockAddCh:
- if block == nil {
- continue
- }
+// HandleBlockProduced handles a block that was produced by us
+// It is handled the same as an imported block in terms of state updates; the only difference
+// is we send a BlockAnnounceMessage to our peers.
+func (s *Service) HandleBlockProduced(block *types.Block, state *rtstorage.TrieState) error {
+ msg := &network.BlockAnnounceMessage{
+ ParentHash: block.Header.ParentHash,
+ Number: block.Header.Number,
+ StateRoot: block.Header.StateRoot,
+ ExtrinsicsRoot: block.Header.ExtrinsicsRoot,
+ Digest: block.Header.Digest,
+ BestBlock: true,
+ }
- if err := s.handleCurrentSlot(block.Header); err != nil {
- logger.Warn("failed to handle epoch for block", "block", block.Header.Hash(), "error", err)
- }
+ s.net.SendMessage(msg)
+ return s.handleBlock(block, state)
+}
- // TODO: add inherent check
- // if err := s.handleChainReorg(prev, block.Header.Hash()); err != nil {
- // logger.Warn("failed to re-add transactions to chain upon re-org", "error", err)
- // }
+func (s *Service) handleBlock(block *types.Block, state *rtstorage.TrieState) error {
+ if block == nil || block.Header == nil || state == nil {
+ return nil
+ }
- if err := s.maintainTransactionPool(block); err != nil {
- logger.Warn("failed to maintain transaction pool", "error", err)
- }
- case <-ctx.Done():
+ // store updates state trie nodes in database
+ err := s.storageState.StoreTrie(state, block.Header)
+ if err != nil {
+ logger.Warn("failed to store state trie for imported block", "block", block.Header.Hash(), "error", err)
+ return err
+ }
+
+ // store block in database
+ 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 {
+ // this is fine
+ } else {
+ return err
+ }
+ }
+
+ logger.Debug("imported block and stored state trie", "block", block.Header.Hash(), "state root", state.MustRoot())
+
+ // handle consensus digests
+ s.digestHandler.HandleDigests(block.Header)
+
+ // check for runtime changes
+ if err := s.handleRuntimeChanges(state); err != nil {
+ logger.Crit("failed to update runtime code", "error", err)
+ return err
+ }
+
+ // check if there was a runtime code substitution
+ if err := s.handleCodeSubstitution(block.Header.Hash()); err != nil {
+ logger.Crit("failed to substitute runtime code", "error", err)
+ return err
+ }
+
+ // check if block production epoch transitioned
+ if err := s.handleCurrentSlot(block.Header); err != nil {
+ logger.Warn("failed to handle epoch for block", "block", block.Header.Hash(), "error", err)
+ return err
+ }
+
+ go func() {
+ s.Lock()
+ defer s.Unlock()
+ if s.ctx.Err() != nil {
return
}
+
+ s.blockAddCh <- block
+ }()
+
+ 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 == "" {
+ return nil
+ }
+
+ logger.Info("π detected runtime code substitution, upgrading...", "block", hash)
+ code := common.MustHexToBytes(value)
+ if len(code) == 0 {
+ return ErrEmptyRuntimeCode
+ }
+
+ err := s.rt.UpdateRuntimeCode(code)
+ if err != nil {
+ return err
+ }
+
+ err = s.codeSubstitutedState.StoreCodeSubstitutedBlockHash(hash)
+ if err != nil {
+ return err
+ }
+
+ return nil
}
func (s *Service) handleCurrentSlot(header *types.Header) error {
@@ -259,50 +366,30 @@ func (s *Service) handleCurrentSlot(header *types.Header) error {
return s.epochState.SetCurrentEpoch(epoch)
}
-// receiveBlocks starts receiving blocks from the BABE session
-func (s *Service) receiveBlocks(ctx context.Context) {
+// handleBlocksAsync handles a block asynchronously; the handling performed by this function
+// does not need to be completed before the next block can be imported.
+func (s *Service) handleBlocksAsync() {
for {
select {
- case block := <-s.blkRec:
- if block.Header == nil {
+ case block := <-s.blockAddCh:
+ if block == nil {
continue
}
- err := s.handleReceivedBlock(&block)
- if err != nil {
- logger.Warn("failed to handle block from BABE session", "err", err)
+ // TODO: add inherent check
+ // if err := s.handleChainReorg(prev, block.Header.Hash()); err != nil {
+ // logger.Warn("failed to re-add transactions to chain upon re-org", "error", err)
+ // }
+
+ if err := s.maintainTransactionPool(block); err != nil {
+ logger.Warn("failed to maintain transaction pool", "error", err)
}
- case <-ctx.Done():
+ case <-s.ctx.Done():
return
}
}
}
-// handleReceivedBlock handles blocks from the BABE session
-func (s *Service) handleReceivedBlock(block *types.Block) (err error) {
- if s.blockState == nil {
- return ErrNilBlockState
- }
-
- logger.Debug("got block from BABE", "header", block.Header, "body", block.Body)
-
- msg := &network.BlockAnnounceMessage{
- ParentHash: block.Header.ParentHash,
- Number: block.Header.Number,
- StateRoot: block.Header.StateRoot,
- ExtrinsicsRoot: block.Header.ExtrinsicsRoot,
- Digest: block.Header.Digest,
- BestBlock: true,
- }
-
- if s.net == nil {
- return
- }
-
- s.net.SendMessage(msg)
- return nil
-}
-
// handleChainReorg checks if there is a chain re-org (ie. new chain head is on a different chain than the
// previous chain head). If there is a re-org, it moves the transactions that were included on the previous
// chain back into the transaction pool.
@@ -451,13 +538,9 @@ func (s *Service) GetRuntimeVersion(bhash *common.Hash) (runtime.Version, error)
return s.rt.Version()
}
-// IsBlockProducer returns true if node is a block producer
-func (s *Service) IsBlockProducer() bool {
- return s.isBlockProducer
-}
-
// HandleSubmittedExtrinsic is used to send a Transaction message containing a Extrinsic @ext
func (s *Service) HandleSubmittedExtrinsic(ext types.Extrinsic) error {
+ logger.Crit("HandleSubmittedExtrinsic")
if s.net == nil {
return nil
}
@@ -470,11 +553,9 @@ func (s *Service) HandleSubmittedExtrinsic(ext types.Extrinsic) error {
return err
}
- if s.isBlockProducer {
- // add transaction to pool
- vtx := transaction.NewValidTransaction(ext, txv)
- s.transactionState.AddToPool(vtx)
- }
+ // add transaction to pool
+ vtx := transaction.NewValidTransaction(ext, txv)
+ s.transactionState.AddToPool(vtx)
// broadcast transaction
msg := &network.TransactionMessage{Extrinsics: []types.Extrinsic{ext}}
diff --git a/dot/core/service_test.go b/dot/core/service_test.go
index 1419509ede..51edd90a1b 100644
--- a/dot/core/service_test.go
+++ b/dot/core/service_test.go
@@ -17,6 +17,7 @@
package core
import (
+ "io/ioutil"
"math/big"
"os"
"sort"
@@ -40,8 +41,8 @@ import (
coremocks "github.com/ChainSafe/gossamer/dot/core/mocks"
)
-func addTestBlocksToState(t *testing.T, depth int, blockState BlockState) []*types.Header {
- return addTestBlocksToStateWithParent(t, blockState.BestBlockHash(), depth, blockState)
+func addTestBlocksToState(t *testing.T, depth int, blockState BlockState) {
+ _ = addTestBlocksToStateWithParent(t, blockState.BestBlockHash(), depth, blockState)
}
func addTestBlocksToStateWithParent(t *testing.T, previousHash common.Hash, depth int, blockState BlockState) []*types.Header {
@@ -100,23 +101,21 @@ func TestStartService(t *testing.T) {
func TestAnnounceBlock(t *testing.T) {
net := new(coremocks.MockNetwork)
- newBlocks := make(chan types.Block)
-
cfg := &Config{
- NewBlocks: newBlocks,
- Network: net,
+ Network: net,
}
s := NewTestService(t, cfg)
err := s.Start()
- require.Nil(t, err)
+ require.NoError(t, err)
defer s.Stop()
// simulate block sent from BABE session
- newBlock := types.Block{
+ newBlock := &types.Block{
Header: &types.Header{
ParentHash: s.blockState.BestBlockHash(),
Number: big.NewInt(1),
+ Digest: types.Digest{types.NewBabeSecondaryPlainPreDigest(0, 1).ToPreRuntimeDigest()},
},
Body: &types.Body{},
}
@@ -132,9 +131,13 @@ func TestAnnounceBlock(t *testing.T) {
net.On("SendMessage", expected)
- newBlocks <- newBlock
- time.Sleep(time.Second * 2)
+ state, err := s.storageState.TrieState(nil)
+ require.NoError(t, err)
+
+ err = s.HandleBlockProduced(newBlock, state)
+ require.NoError(t, err)
+ time.Sleep(time.Second)
net.AssertCalled(t, "SendMessage", expected)
}
@@ -425,15 +428,6 @@ func TestService_GetRuntimeVersion(t *testing.T) {
require.Equal(t, rtExpected, rtv)
}
-func TestService_IsBlockProducer(t *testing.T) {
- cfg := &Config{
- IsBlockProducer: false,
- }
- s := NewTestService(t, cfg)
- bp := s.IsBlockProducer()
- require.Equal(t, false, bp)
-}
-
func TestService_HandleSubmittedExtrinsic(t *testing.T) {
s := NewTestService(t, nil)
@@ -457,3 +451,58 @@ func TestService_GetMetadata(t *testing.T) {
require.NoError(t, err)
require.Greater(t, len(res), 10000)
}
+
+func TestService_HandleRuntimeChanges(t *testing.T) {
+ s := NewTestService(t, nil)
+ codeHashBefore := s.codeHash
+
+ testRuntime, err := ioutil.ReadFile(runtime.POLKADOT_RUNTIME_FP)
+ require.NoError(t, err)
+
+ ts, err := s.storageState.TrieState(nil)
+ require.NoError(t, err)
+
+ ts.Set(common.CodeKey, testRuntime)
+ err = s.handleRuntimeChanges(ts)
+ require.NoError(t, err)
+ codeHashAfter := s.codeHash
+ require.NotEqualf(t, codeHashBefore, codeHashAfter, "expected different code hash after runtime update")
+}
+
+func TestService_HandleCodeSubstitutes(t *testing.T) {
+ s := NewTestService(t, nil)
+
+ testRuntime, err := ioutil.ReadFile(runtime.POLKADOT_RUNTIME_FP)
+ require.NoError(t, err)
+
+ blockHash := common.MustHexToHash("0x86aa36a140dfc449c30dbce16ce0fea33d5c3786766baa764e33f336841b9e29") // hash for known test code substitution
+ s.codeSubstitute = map[common.Hash]string{
+ blockHash: common.BytesToHex(testRuntime),
+ }
+
+ err = s.handleCodeSubstitution(blockHash)
+ require.NoError(t, err)
+ codSub := s.codeSubstitutedState.LoadCodeSubstitutedBlockHash()
+ require.Equal(t, blockHash, codSub)
+}
+
+func TestService_HandleRuntimeChangesAfterCodeSubstitutes(t *testing.T) {
+ s := NewTestService(t, nil)
+ codeHashBefore := s.codeHash
+ blockHash := common.MustHexToHash("0x86aa36a140dfc449c30dbce16ce0fea33d5c3786766baa764e33f336841b9e29") // hash for known test code substitution
+
+ err := s.handleCodeSubstitution(blockHash)
+ require.NoError(t, err)
+ require.Equal(t, codeHashBefore, s.codeHash) // codeHash should remain unchanged after code substitute
+
+ testRuntime, err := ioutil.ReadFile(runtime.POLKADOT_RUNTIME_FP)
+ require.NoError(t, err)
+
+ ts, err := s.storageState.TrieState(nil)
+ require.NoError(t, err)
+
+ ts.Set(common.CodeKey, testRuntime)
+ err = s.handleRuntimeChanges(ts)
+ require.NoError(t, err)
+ require.NotEqualf(t, codeHashBefore, s.codeHash, "expected different code hash after runtime update") // codeHash should change after runtime change
+}
diff --git a/dot/core/test_helpers.go b/dot/core/test_helpers.go
index a7a4a226a9..fc9b786136 100644
--- a/dot/core/test_helpers.go
+++ b/dot/core/test_helpers.go
@@ -32,11 +32,10 @@ import (
"github.com/ChainSafe/gossamer/lib/runtime/wasmer"
"github.com/ChainSafe/gossamer/lib/trie"
log "github.com/ChainSafe/log15"
- "github.com/stretchr/testify/mock"
- "github.com/stretchr/testify/require"
- // importing packagemocks
coremocks "github.com/ChainSafe/gossamer/dot/core/mocks"
+ "github.com/stretchr/testify/mock"
+ "github.com/stretchr/testify/require"
)
func newTestGenesisWithTrieAndHeader(t *testing.T) (*genesis.Genesis, *trie.Trie, *types.Header) {
@@ -57,11 +56,12 @@ func newTestGenesisWithTrieAndHeader(t *testing.T) (*genesis.Genesis, *trie.Trie
// NewTestService creates a new test core service
func NewTestService(t *testing.T, cfg *Config) *Service {
if cfg == nil {
- cfg = &Config{
- IsBlockProducer: false,
- }
+ cfg = &Config{}
}
+ cfg.DigestHandler = new(coremocks.MockDigestHandler)
+ cfg.DigestHandler.(*coremocks.MockDigestHandler).On("HandleDigests", mock.AnythingOfType("*types.Header"))
+
if cfg.Keystore == nil {
cfg.Keystore = keystore.NewGlobalKeystore()
kp, err := sr25519.GenerateKeypair()
@@ -71,16 +71,6 @@ func NewTestService(t *testing.T, cfg *Config) *Service {
cfg.Keystore.Acco.Insert(kp)
}
- if cfg.NewBlocks == nil {
- cfg.NewBlocks = make(chan types.Block)
- }
-
- if cfg.Verifier == nil {
- verifier := new(coremocks.MockVerifier)
- verifier.On("SetOnDisabled", mock.AnythingOfType("uint32"), mock.AnythingOfType("*types.Header")).Return(nil)
- cfg.Verifier = nil
- }
-
cfg.LogLvl = 3
var stateSrvc *state.Service
@@ -89,7 +79,7 @@ func NewTestService(t *testing.T, cfg *Config) *Service {
gen, genTrie, genHeader := newTestGenesisWithTrieAndHeader(t)
- if cfg.BlockState == nil || cfg.StorageState == nil || cfg.TransactionState == nil || cfg.EpochState == nil {
+ if cfg.BlockState == nil || cfg.StorageState == nil || cfg.TransactionState == nil || cfg.EpochState == nil || cfg.CodeSubstitutedState == nil {
config := state.Config{
Path: testDatadirPath,
LogLevel: log.LvlInfo,
@@ -120,6 +110,10 @@ func NewTestService(t *testing.T, cfg *Config) *Service {
cfg.EpochState = stateSrvc.Epoch
}
+ if cfg.CodeSubstitutedState == nil {
+ cfg.CodeSubstitutedState = stateSrvc.Base
+ }
+
if cfg.Runtime == nil {
rtCfg := &wasmer.Config{}
rtCfg.Storage, err = rtstorage.NewTrieState(genTrie)
@@ -140,8 +134,23 @@ func NewTestService(t *testing.T, cfg *Config) *Service {
cfg.Network = createTestNetworkService(t, config)
}
+ if cfg.CodeSubstitutes == nil {
+ cfg.CodeSubstitutes = make(map[common.Hash]string)
+
+ genesisData, err := cfg.CodeSubstitutedState.(*state.BaseState).LoadGenesisData() //nolint
+ require.NoError(t, err)
+
+ for k, v := range genesisData.CodeSubstitutes {
+ cfg.CodeSubstitutes[common.MustHexToHash(k)] = v
+ }
+ }
+
+ if cfg.CodeSubstitutedState == nil {
+ cfg.CodeSubstitutedState = stateSrvc.Base
+ }
+
s, err := NewService(cfg)
- require.Nil(t, err)
+ require.NoError(t, err)
if net, ok := cfg.Network.(*network.Service); ok {
net.SetTransactionHandler(s)
diff --git a/dot/core/digest.go b/dot/digest/digest.go
similarity index 81%
rename from dot/core/digest.go
rename to dot/digest/digest.go
index 3034706499..f48c2d9c5a 100644
--- a/dot/core/digest.go
+++ b/dot/digest/digest.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the gossamer library. If not, see .
-package core
+package digest
import (
"context"
@@ -23,12 +23,20 @@ import (
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/scale"
+ "github.com/ChainSafe/gossamer/lib/services"
+
+ log "github.com/ChainSafe/log15"
)
var maxUint64 = uint64(2^64) - 1
-// DigestHandler is used to handle consensus messages and relevant authority updates to BABE and GRANDPA
-type DigestHandler struct {
+var (
+ _ services.Service = &Handler{}
+ logger log.Logger = log.New("pkg", "digest") // TODO: add to config options
+)
+
+// Handler is used to handle consensus messages and relevant authority updates to BABE and GRANDPA
+type Handler struct {
ctx context.Context
cancel context.CancelFunc
@@ -36,8 +44,6 @@ type DigestHandler struct {
blockState BlockState
epochState EpochState
grandpaState GrandpaState
- babe BlockProducer
- verifier Verifier
// block notification channels
imported chan *types.Block
@@ -65,8 +71,8 @@ type resume struct {
atBlock *big.Int
}
-// NewDigestHandler returns a new DigestHandler
-func NewDigestHandler(blockState BlockState, epochState EpochState, grandpaState GrandpaState, babe BlockProducer, verifier Verifier) (*DigestHandler, error) {
+// NewHandler returns a new Handler
+func NewHandler(blockState BlockState, epochState EpochState, grandpaState GrandpaState) (*Handler, error) {
imported := make(chan *types.Block, 16)
finalised := make(chan *types.FinalisationInfo, 16)
iid, err := blockState.RegisterImportedChannel(imported)
@@ -81,14 +87,12 @@ func NewDigestHandler(blockState BlockState, epochState EpochState, grandpaState
ctx, cancel := context.WithCancel(context.Background())
- return &DigestHandler{
+ return &Handler{
ctx: ctx,
cancel: cancel,
blockState: blockState,
epochState: epochState,
grandpaState: grandpaState,
- babe: babe,
- verifier: verifier,
imported: imported,
importedID: iid,
finalised: finalised,
@@ -96,15 +100,15 @@ func NewDigestHandler(blockState BlockState, epochState EpochState, grandpaState
}, nil
}
-// Start starts the DigestHandler
-func (h *DigestHandler) Start() error {
+// Start starts the Handler
+func (h *Handler) Start() error {
go h.handleBlockImport(h.ctx)
go h.handleBlockFinalisation(h.ctx)
return nil
}
-// Stop stops the DigestHandler
-func (h *DigestHandler) Stop() error {
+// Stop stops the Handler
+func (h *Handler) Stop() error {
h.cancel()
h.blockState.UnregisterImportedChannel(h.importedID)
h.blockState.UnregisterFinalizedChannel(h.finalisedID)
@@ -115,7 +119,7 @@ func (h *DigestHandler) Stop() error {
// NextGrandpaAuthorityChange returns the block number of the next upcoming grandpa authorities change.
// It returns 0 if no change is scheduled.
-func (h *DigestHandler) NextGrandpaAuthorityChange() uint64 {
+func (h *Handler) NextGrandpaAuthorityChange() uint64 {
next := maxUint64
if h.grandpaScheduledChange != nil {
@@ -137,8 +141,25 @@ func (h *DigestHandler) NextGrandpaAuthorityChange() uint64 {
return next
}
-// HandleConsensusDigest is the function used by the syncer to handle a consensus digest
-func (h *DigestHandler) HandleConsensusDigest(d *types.ConsensusDigest, header *types.Header) error {
+// HandleDigests handles consensus digests for an imported block
+func (h *Handler) HandleDigests(header *types.Header) {
+ for i, d := range header.Digest {
+ if d.Type() == types.ConsensusDigestType {
+ cd, ok := d.(*types.ConsensusDigest)
+ if !ok {
+ logger.Error("handleDigests", "block number", header.Number, "index", i, "error", "cannot cast invalid consensus digest item")
+ continue
+ }
+
+ err := h.handleConsensusDigest(cd, header)
+ if err != nil {
+ logger.Error("handleDigests", "block number", header.Number, "index", i, "digest", cd, "error", err)
+ }
+ }
+ }
+}
+
+func (h *Handler) handleConsensusDigest(d *types.ConsensusDigest, header *types.Header) error {
t := d.DataType()
if d.ConsensusEngineID == types.GrandpaEngineID {
@@ -174,7 +195,7 @@ func (h *DigestHandler) HandleConsensusDigest(d *types.ConsensusDigest, header *
return errors.New("unknown consensus engine ID")
}
-func (h *DigestHandler) handleBlockImport(ctx context.Context) {
+func (h *Handler) handleBlockImport(ctx context.Context) {
for {
select {
case block := <-h.imported:
@@ -192,7 +213,7 @@ func (h *DigestHandler) handleBlockImport(ctx context.Context) {
}
}
-func (h *DigestHandler) handleBlockFinalisation(ctx context.Context) {
+func (h *Handler) handleBlockFinalisation(ctx context.Context) {
for {
select {
case info := <-h.finalised:
@@ -210,7 +231,7 @@ func (h *DigestHandler) handleBlockFinalisation(ctx context.Context) {
}
}
-func (h *DigestHandler) handleGrandpaChangesOnImport(num *big.Int) error {
+func (h *Handler) handleGrandpaChangesOnImport(num *big.Int) error {
resume := h.grandpaResume
if resume != nil && num.Cmp(resume.atBlock) > -1 {
h.grandpaResume = nil
@@ -235,7 +256,7 @@ func (h *DigestHandler) handleGrandpaChangesOnImport(num *big.Int) error {
return nil
}
-func (h *DigestHandler) handleGrandpaChangesOnFinalization(num *big.Int) error {
+func (h *Handler) handleGrandpaChangesOnFinalization(num *big.Int) error {
pause := h.grandpaPause
if pause != nil && num.Cmp(pause.atBlock) > -1 {
h.grandpaPause = nil
@@ -262,7 +283,7 @@ func (h *DigestHandler) handleGrandpaChangesOnFinalization(num *big.Int) error {
return nil
}
-func (h *DigestHandler) handleScheduledChange(d *types.ConsensusDigest, header *types.Header) error {
+func (h *Handler) handleScheduledChange(d *types.ConsensusDigest, header *types.Header) error {
curr, err := h.blockState.BestBlockHeader()
if err != nil {
return err
@@ -304,7 +325,7 @@ func (h *DigestHandler) handleScheduledChange(d *types.ConsensusDigest, header *
)
}
-func (h *DigestHandler) handleForcedChange(d *types.ConsensusDigest, header *types.Header) error {
+func (h *Handler) handleForcedChange(d *types.ConsensusDigest, header *types.Header) error {
if d.ConsensusEngineID != types.GrandpaEngineID {
return nil
}
@@ -345,7 +366,7 @@ func (h *DigestHandler) handleForcedChange(d *types.ConsensusDigest, header *typ
)
}
-func (h *DigestHandler) handlePause(d *types.ConsensusDigest) error {
+func (h *Handler) handlePause(d *types.ConsensusDigest) error {
curr, err := h.blockState.BestBlockHeader()
if err != nil {
return err
@@ -367,7 +388,7 @@ func (h *DigestHandler) handlePause(d *types.ConsensusDigest) error {
return h.grandpaState.SetNextPause(h.grandpaPause.atBlock)
}
-func (h *DigestHandler) handleResume(d *types.ConsensusDigest) error {
+func (h *Handler) handleResume(d *types.ConsensusDigest) error {
curr, err := h.blockState.BestBlockHeader()
if err != nil {
return err
@@ -403,25 +424,13 @@ func newGrandpaChange(raw []*types.GrandpaAuthoritiesRaw, delay uint32, currBloc
}, nil
}
-func (h *DigestHandler) handleBABEOnDisabled(d *types.ConsensusDigest, header *types.Header) error {
- od := new(types.BABEOnDisabled)
- _, err := scale.Decode(d.Data[1:], od)
- if err != nil {
- return err
- }
-
+func (h *Handler) handleBABEOnDisabled(d *types.ConsensusDigest, _ *types.Header) error {
+ od := &types.BABEOnDisabled{}
logger.Debug("handling BABEOnDisabled", "data", od)
-
- err = h.verifier.SetOnDisabled(od.ID, header)
-
- if err != nil {
- return err
- }
- h.babe.SetOnDisabled(od.ID)
return nil
}
-func (h *DigestHandler) handleNextEpochData(d *types.ConsensusDigest, header *types.Header) error {
+func (h *Handler) handleNextEpochData(d *types.ConsensusDigest, header *types.Header) error {
od := &types.NextEpochData{}
dec, err := scale.Decode(d.Data[1:], od)
if err != nil {
@@ -446,7 +455,7 @@ func (h *DigestHandler) handleNextEpochData(d *types.ConsensusDigest, header *ty
return h.epochState.SetEpochData(currEpoch+1, data)
}
-func (h *DigestHandler) handleNextConfigData(d *types.ConsensusDigest, header *types.Header) error {
+func (h *Handler) handleNextConfigData(d *types.ConsensusDigest, header *types.Header) error {
od := &types.NextConfigData{}
dec, err := scale.Decode(d.Data[1:], od)
if err != nil {
diff --git a/dot/core/digest_test.go b/dot/digest/digest_test.go
similarity index 74%
rename from dot/core/digest_test.go
rename to dot/digest/digest_test.go
index 308b06497e..7af5a21206 100644
--- a/dot/core/digest_test.go
+++ b/dot/digest/digest_test.go
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the gossamer library. If not, see .
-package core
+package digest
import (
"io/ioutil"
@@ -24,18 +24,65 @@ import (
"github.com/ChainSafe/gossamer/dot/state"
"github.com/ChainSafe/gossamer/dot/types"
+ "github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/crypto/ed25519"
"github.com/ChainSafe/gossamer/lib/crypto/sr25519"
+ "github.com/ChainSafe/gossamer/lib/genesis"
"github.com/ChainSafe/gossamer/lib/keystore"
-
- . "github.com/ChainSafe/gossamer/dot/core/mocks"
+ "github.com/ChainSafe/gossamer/lib/trie"
log "github.com/ChainSafe/log15"
- "github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
-func newTestDigestHandler(t *testing.T, withBABE, withGrandpa bool) *DigestHandler { //nolint
+// TODO: use these from core?
+func addTestBlocksToState(t *testing.T, depth int, blockState BlockState) []*types.Header {
+ return addTestBlocksToStateWithParent(t, blockState.(*state.BlockState).BestBlockHash(), depth, blockState)
+}
+
+func addTestBlocksToStateWithParent(t *testing.T, previousHash common.Hash, depth int, blockState BlockState) []*types.Header {
+ prevHeader, err := blockState.(*state.BlockState).GetHeader(previousHash)
+ require.NoError(t, err)
+ previousNum := prevHeader.Number
+
+ headers := []*types.Header{}
+
+ for i := 1; i <= depth; i++ {
+ block := &types.Block{
+ Header: &types.Header{
+ ParentHash: previousHash,
+ Number: big.NewInt(int64(i)).Add(previousNum, big.NewInt(int64(i))),
+ Digest: types.Digest{},
+ },
+ Body: &types.Body{},
+ }
+
+ previousHash = block.Header.Hash()
+
+ err := blockState.(*state.BlockState).AddBlock(block)
+ require.NoError(t, err)
+ headers = append(headers, block.Header)
+ }
+
+ return headers
+}
+
+func newTestGenesisWithTrieAndHeader(t *testing.T) (*genesis.Genesis, *trie.Trie, *types.Header) {
+ gen, err := genesis.NewGenesisFromJSONRaw("../../chain/gssmr/genesis.json")
+ if err != nil {
+ gen, err = genesis.NewGenesisFromJSONRaw("../../../chain/gssmr/genesis.json")
+ require.NoError(t, err)
+ }
+
+ genTrie, err := genesis.NewTrieFromGenesis(gen)
+ require.NoError(t, err)
+
+ genesisHeader, err := types.NewHeader(common.NewHash([]byte{0}), genTrie.MustHash(), trie.EmptyHash, big.NewInt(0), types.Digest{})
+ require.NoError(t, err)
+ return gen, genTrie, genesisHeader
+}
+
+func newTestHandler(t *testing.T, withBABE, withGrandpa bool) *Handler { //nolint
testDatadirPath, err := ioutil.TempDir("/tmp", "test-datadir-*")
require.NoError(t, err)
@@ -53,23 +100,13 @@ func newTestDigestHandler(t *testing.T, withBABE, withGrandpa bool) *DigestHandl
err = stateSrvc.Start()
require.NoError(t, err)
- var bp *MockBlockProducer
- if withBABE {
- bp = new(MockBlockProducer)
- blockC := make(chan types.Block)
- bp.On("GetBlockChannel", nil).Return(blockC)
- }
-
- verifier := new(MockVerifier)
- verifier.On("SetOnDisabled", mock.Anything, mock.Anything).Return(nil)
-
- dh, err := NewDigestHandler(stateSrvc.Block, stateSrvc.Epoch, stateSrvc.Grandpa, nil, nil)
+ dh, err := NewHandler(stateSrvc.Block, stateSrvc.Epoch, stateSrvc.Grandpa)
require.NoError(t, err)
return dh
}
-func TestDigestHandler_GrandpaScheduledChange(t *testing.T) {
- handler := newTestDigestHandler(t, false, true)
+func TestHandler_GrandpaScheduledChange(t *testing.T) {
+ handler := newTestHandler(t, false, true)
handler.Start()
defer handler.Stop()
@@ -95,21 +132,21 @@ func TestDigestHandler_GrandpaScheduledChange(t *testing.T) {
Number: big.NewInt(1),
}
- err = handler.HandleConsensusDigest(d, header)
+ err = handler.handleConsensusDigest(d, header)
require.NoError(t, err)
headers := addTestBlocksToState(t, 2, handler.blockState)
for _, h := range headers {
- handler.blockState.SetFinalizedHash(h.Hash(), 0, 0)
+ handler.blockState.(*state.BlockState).SetFinalizedHash(h.Hash(), 0, 0)
}
// authorities should change on start of block 3 from start
headers = addTestBlocksToState(t, 1, handler.blockState)
for _, h := range headers {
- handler.blockState.SetFinalizedHash(h.Hash(), 0, 0)
+ handler.blockState.(*state.BlockState).SetFinalizedHash(h.Hash(), 0, 0)
}
- time.Sleep(time.Millisecond * 100)
+ time.Sleep(time.Millisecond * 500)
setID, err := handler.grandpaState.(*state.GrandpaState).GetCurrentSetID()
require.NoError(t, err)
require.Equal(t, uint64(1), setID)
@@ -121,8 +158,8 @@ func TestDigestHandler_GrandpaScheduledChange(t *testing.T) {
require.Equal(t, expected, auths)
}
-func TestDigestHandler_GrandpaForcedChange(t *testing.T) {
- handler := newTestDigestHandler(t, false, true)
+func TestHandler_GrandpaForcedChange(t *testing.T) {
+ handler := newTestHandler(t, false, true)
handler.Start()
defer handler.Stop()
@@ -148,7 +185,7 @@ func TestDigestHandler_GrandpaForcedChange(t *testing.T) {
Number: big.NewInt(1),
}
- err = handler.HandleConsensusDigest(d, header)
+ err = handler.handleConsensusDigest(d, header)
require.NoError(t, err)
addTestBlocksToState(t, 3, handler.blockState)
@@ -168,8 +205,8 @@ func TestDigestHandler_GrandpaForcedChange(t *testing.T) {
require.Equal(t, expected, auths)
}
-func TestDigestHandler_GrandpaPauseAndResume(t *testing.T) {
- handler := newTestDigestHandler(t, false, true)
+func TestHandler_GrandpaPauseAndResume(t *testing.T) {
+ handler := newTestHandler(t, false, true)
handler.Start()
defer handler.Stop()
@@ -185,7 +222,7 @@ func TestDigestHandler_GrandpaPauseAndResume(t *testing.T) {
Data: data,
}
- err = handler.HandleConsensusDigest(d, nil)
+ err = handler.handleConsensusDigest(d, nil)
require.NoError(t, err)
nextPause, err := handler.grandpaState.(*state.GrandpaState).GetNextPause()
require.NoError(t, err)
@@ -193,7 +230,7 @@ func TestDigestHandler_GrandpaPauseAndResume(t *testing.T) {
headers := addTestBlocksToState(t, 3, handler.blockState)
for _, h := range headers {
- handler.blockState.SetFinalizedHash(h.Hash(), 0, 0)
+ handler.blockState.(*state.BlockState).SetFinalizedHash(h.Hash(), 0, 0)
}
time.Sleep(time.Millisecond * 100)
@@ -211,7 +248,7 @@ func TestDigestHandler_GrandpaPauseAndResume(t *testing.T) {
Data: data,
}
- err = handler.HandleConsensusDigest(d, nil)
+ err = handler.handleConsensusDigest(d, nil)
require.NoError(t, err)
addTestBlocksToState(t, 3, handler.blockState)
@@ -224,7 +261,7 @@ func TestDigestHandler_GrandpaPauseAndResume(t *testing.T) {
}
func TestNextGrandpaAuthorityChange_OneChange(t *testing.T) {
- handler := newTestDigestHandler(t, false, true)
+ handler := newTestHandler(t, false, true)
handler.Start()
defer handler.Stop()
@@ -245,7 +282,7 @@ func TestNextGrandpaAuthorityChange_OneChange(t *testing.T) {
Number: big.NewInt(1),
}
- err = handler.HandleConsensusDigest(d, header)
+ err = handler.handleConsensusDigest(d, header)
require.NoError(t, err)
next := handler.NextGrandpaAuthorityChange()
@@ -260,7 +297,7 @@ func TestNextGrandpaAuthorityChange_OneChange(t *testing.T) {
}
func TestNextGrandpaAuthorityChange_MultipleChanges(t *testing.T) {
- handler := newTestDigestHandler(t, false, true)
+ handler := newTestHandler(t, false, true)
handler.Start()
defer handler.Stop()
@@ -285,7 +322,7 @@ func TestNextGrandpaAuthorityChange_MultipleChanges(t *testing.T) {
Number: big.NewInt(1),
}
- err = handler.HandleConsensusDigest(d, header)
+ err = handler.handleConsensusDigest(d, header)
require.NoError(t, err)
nextSetID := uint64(1)
@@ -311,7 +348,7 @@ func TestNextGrandpaAuthorityChange_MultipleChanges(t *testing.T) {
Data: data,
}
- err = handler.HandleConsensusDigest(d, header)
+ err = handler.handleConsensusDigest(d, header)
require.NoError(t, err)
next := handler.NextGrandpaAuthorityChange()
@@ -324,22 +361,12 @@ func TestNextGrandpaAuthorityChange_MultipleChanges(t *testing.T) {
require.Equal(t, expected, auths)
}
-func TestDigestHandler_HandleBABEOnDisabled(t *testing.T) {
- handler := newTestDigestHandler(t, true, false)
-
- babemock := new(MockBlockProducer)
- babemock.On("SetOnDisabled", uint32(7))
-
+func TestHandler_HandleBABEOnDisabled(t *testing.T) {
+ handler := newTestHandler(t, true, false)
header := &types.Header{
Number: big.NewInt(1),
}
- verifier := new(MockVerifier)
- verifier.On("SetOnDisabled", uint32(7), header).Return(nil)
-
- handler.babe = babemock
- handler.verifier = verifier
-
digest := &types.BABEOnDisabled{
ID: 7,
}
@@ -352,11 +379,8 @@ func TestDigestHandler_HandleBABEOnDisabled(t *testing.T) {
Data: data,
}
- err = handler.HandleConsensusDigest(d, header)
-
+ err = handler.handleConsensusDigest(d, header)
require.NoError(t, err)
-
- babemock.AssertCalled(t, "SetOnDisabled", uint32(7))
}
func createHeaderWithPreDigest(slotNumber uint64) *types.Header {
@@ -372,8 +396,8 @@ func createHeaderWithPreDigest(slotNumber uint64) *types.Header {
}
}
-func TestDigestHandler_HandleNextEpochData(t *testing.T) {
- handler := newTestDigestHandler(t, true, false)
+func TestHandler_HandleNextEpochData(t *testing.T) {
+ handler := newTestHandler(t, true, false)
handler.Start()
defer handler.Stop()
@@ -405,7 +429,7 @@ func TestDigestHandler_HandleNextEpochData(t *testing.T) {
header := createHeaderWithPreDigest(10)
- err = handler.HandleConsensusDigest(d, header)
+ err = handler.handleConsensusDigest(d, header)
require.NoError(t, err)
stored, err := handler.epochState.(*state.EpochState).GetEpochData(1)
@@ -415,8 +439,8 @@ func TestDigestHandler_HandleNextEpochData(t *testing.T) {
require.Equal(t, res, stored)
}
-func TestDigestHandler_HandleNextConfigData(t *testing.T) {
- handler := newTestDigestHandler(t, true, false)
+func TestHandler_HandleNextConfigData(t *testing.T) {
+ handler := newTestHandler(t, true, false)
handler.Start()
defer handler.Stop()
@@ -436,7 +460,7 @@ func TestDigestHandler_HandleNextConfigData(t *testing.T) {
header := createHeaderWithPreDigest(10)
- err = handler.HandleConsensusDigest(d, header)
+ err = handler.handleConsensusDigest(d, header)
require.NoError(t, err)
stored, err := handler.epochState.(*state.EpochState).GetConfigData(1)
diff --git a/dot/digest/interface.go b/dot/digest/interface.go
new file mode 100644
index 0000000000..f3df3c7f60
--- /dev/null
+++ b/dot/digest/interface.go
@@ -0,0 +1,49 @@
+// Copyright 2019 ChainSafe Systems (ON) Corp.
+// This file is part of gossamer.
+//
+// The gossamer library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The gossamer library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the gossamer library. If not, see .
+
+package digest
+
+import (
+ "math/big"
+
+ "github.com/ChainSafe/gossamer/dot/types"
+ "github.com/ChainSafe/gossamer/lib/grandpa"
+)
+
+// BlockState interface for block state methods
+type BlockState interface {
+ BestBlockHeader() (*types.Header, error)
+ RegisterImportedChannel(ch chan<- *types.Block) (byte, error)
+ UnregisterImportedChannel(id byte)
+ RegisterFinalizedChannel(ch chan<- *types.FinalisationInfo) (byte, error)
+ UnregisterFinalizedChannel(id byte)
+}
+
+// EpochState is the interface for state.EpochState
+type EpochState interface {
+ GetEpochForBlock(header *types.Header) (uint64, error)
+ SetEpochData(epoch uint64, info *types.EpochData) error
+ SetConfigData(epoch uint64, info *types.ConfigData) error
+}
+
+// GrandpaState is the interface for the state.GrandpaState
+type GrandpaState interface {
+ SetNextChange(authorities []*grandpa.Voter, number *big.Int) error
+ IncrementSetID() error
+ SetNextPause(number *big.Int) error
+ SetNextResume(number *big.Int) error
+ GetCurrentSetID() (uint64, error)
+}
diff --git a/dot/node.go b/dot/node.go
index 4da6ec9348..04572047bc 100644
--- a/dot/node.go
+++ b/dot/node.go
@@ -227,8 +227,6 @@ func NewNode(cfg *Config, ks *keystore.GlobalKeystore, stopFunc func()) (*Node,
return nil, ErrNoKeysProvided
}
- // Node Services
-
logger.Info(
"πΈοΈ initialising node services...",
"name", cfg.Global.Name,
@@ -236,20 +234,16 @@ func NewNode(cfg *Config, ks *keystore.GlobalKeystore, stopFunc func()) (*Node,
"basepath", cfg.Global.BasePath,
)
- var nodeSrvcs []services.Service
-
- // State Service
+ var (
+ nodeSrvcs []services.Service
+ networkSrvc *network.Service
+ )
- // create state service and append state service to node services
stateSrvc, err := createStateService(cfg)
-
if err != nil {
return nil, fmt.Errorf("failed to create state service: %s", err)
}
- // Network Service
- var networkSrvc *network.Service
-
// check if network service is enabled
if enabled := networkServiceEnabled(cfg); enabled {
// create network service and append network service to node services
@@ -274,65 +268,51 @@ func NewNode(cfg *Config, ks *keystore.GlobalKeystore, stopFunc func()) (*Node,
return nil, err
}
- // create BABE service
- bp, err := createBABEService(cfg, rt, stateSrvc, ks.Babe)
+ dh, err := createDigestHandler(stateSrvc)
if err != nil {
return nil, err
}
+ nodeSrvcs = append(nodeSrvcs, dh)
- nodeSrvcs = append(nodeSrvcs, bp)
+ coreSrvc, err := createCoreService(cfg, rt, ks, stateSrvc, networkSrvc, dh)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create core service: %s", err)
+ }
+ nodeSrvcs = append(nodeSrvcs, coreSrvc)
- dh, err := createDigestHandler(stateSrvc, bp, ver)
+ bp, err := createBABEService(cfg, rt, stateSrvc, ks.Babe, coreSrvc)
if err != nil {
return nil, err
}
- nodeSrvcs = append(nodeSrvcs, dh)
+ nodeSrvcs = append(nodeSrvcs, bp)
- // create GRANDPA service
fg, err := createGRANDPAService(cfg, rt, stateSrvc, dh, ks.Gran, networkSrvc)
if err != nil {
return nil, err
}
nodeSrvcs = append(nodeSrvcs, fg)
- // Syncer
- syncer, err := newSyncService(cfg, stateSrvc, bp, fg, dh, ver, rt)
+ syncer, err := newSyncService(cfg, stateSrvc, fg, ver, rt, coreSrvc)
if err != nil {
return nil, err
}
- // Core Service
-
- // create core service and append core service to node services
- coreSrvc, err := createCoreService(cfg, bp, ver, rt, ks, stateSrvc, networkSrvc)
- if err != nil {
- return nil, fmt.Errorf("failed to create core service: %s", err)
- }
- nodeSrvcs = append(nodeSrvcs, coreSrvc)
-
if networkSrvc != nil {
networkSrvc.SetSyncer(syncer)
networkSrvc.SetTransactionHandler(coreSrvc)
}
- // System Service
-
- // create system service and append to node services
sysSrvc, err := createSystemService(&cfg.System, stateSrvc)
if err != nil {
return nil, fmt.Errorf("failed to create system service: %s", err)
}
nodeSrvcs = append(nodeSrvcs, sysSrvc)
- // RPC Service
-
// check if rpc service is enabled
if enabled := cfg.RPC.Enabled; enabled {
- // create rpc service and append rpc service to node services
rpcSrvc := createRPCService(cfg, stateSrvc, coreSrvc, networkSrvc, bp, rt, sysSrvc)
nodeSrvcs = append(nodeSrvcs, rpcSrvc)
} else {
- // do not create or append rpc service if rpc service is not enabled
logger.Debug("rpc service disabled by default", "rpc", enabled)
}
diff --git a/dot/node_test.go b/dot/node_test.go
index 455bebb1d4..253051cc2f 100644
--- a/dot/node_test.go
+++ b/dot/node_test.go
@@ -188,7 +188,9 @@ func TestStartNode(t *testing.T) {
require.NoError(t, err)
go func() {
- time.Sleep(time.Second)
+ // TODO: need to wait until all services are started so that wg.Add is called, otherwise
+ // will call wg.Done before the counter is at 1
+ time.Sleep(time.Second * 15)
node.Stop()
}()
diff --git a/dot/rpc/modules/api.go b/dot/rpc/modules/api.go
index ce7356d1d6..5e459b8e83 100644
--- a/dot/rpc/modules/api.go
+++ b/dot/rpc/modules/api.go
@@ -70,7 +70,6 @@ type CoreAPI interface {
InsertKey(kp crypto.Keypair)
HasKey(pubKeyStr string, keyType string) (bool, error)
GetRuntimeVersion(bhash *common.Hash) (runtime.Version, error)
- IsBlockProducer() bool
HandleSubmittedExtrinsic(types.Extrinsic) error
GetMetadata(bhash *common.Hash) ([]byte, error)
}
diff --git a/dot/rpc/modules/author.go b/dot/rpc/modules/author.go
index 5b9d68c4bd..02dc2acf0e 100644
--- a/dot/rpc/modules/author.go
+++ b/dot/rpc/modules/author.go
@@ -165,7 +165,7 @@ func (cm *AuthorModule) SubmitExtrinsic(r *http.Request, req *Extrinsic, res *Ex
return err
}
ext := types.Extrinsic(extBytes)
- cm.logger.Trace("[rpc]", "extrinsic", ext)
+ cm.logger.Crit("[rpc]", "extrinsic", ext)
err = cm.coreAPI.HandleSubmittedExtrinsic(ext)
*res = ExtrinsicHashResponse(ext.Hash().String())
diff --git a/dot/rpc/modules/author_test.go b/dot/rpc/modules/author_test.go
index ff3f48f785..ef4e1bf193 100644
--- a/dot/rpc/modules/author_test.go
+++ b/dot/rpc/modules/author_test.go
@@ -276,21 +276,23 @@ func newCoreService(t *testing.T, srvc *state.Service) *core.Service {
}
cfg := &core.Config{
- Runtime: rt,
- Keystore: ks,
- TransactionState: srvc.Transaction,
- IsBlockProducer: false,
- BlockState: srvc.Block,
- StorageState: srvc.Storage,
- EpochState: srvc.Epoch,
- Network: &mockNetwork{},
+ Runtime: rt,
+ Keystore: ks,
+ TransactionState: srvc.Transaction,
+ BlockState: srvc.Block,
+ StorageState: srvc.Storage,
+ EpochState: srvc.Epoch,
+ Network: &mockNetwork{},
+ CodeSubstitutedState: srvc.Base,
}
return core.NewTestService(t, cfg)
}
func setupAuthModule(t *testing.T, txq *state.TransactionState) *AuthorModule {
+ fmt.Println("calling setupAuthModule")
cs := newCoreService(t, nil)
+ fmt.Println("called newCoreService")
rt := wasmer.NewTestInstance(t, runtime.NODE_RUNTIME)
t.Cleanup(func() {
rt.Stop()
diff --git a/dot/rpc/modules/chain_test.go b/dot/rpc/modules/chain_test.go
index 595f723e47..a6d2ad6279 100644
--- a/dot/rpc/modules/chain_test.go
+++ b/dot/rpc/modules/chain_test.go
@@ -265,7 +265,13 @@ func TestChainGetFinalizedHeadByRound(t *testing.T) {
expected := genesisHeader.Hash()
require.Equal(t, common.BytesToHex(expected[:]), res)
- testhash := common.Hash{1, 2, 3, 4}
+ header := &types.Header{
+ Number: big.NewInt(1),
+ }
+ err = state.Block.SetHeader(header)
+ require.NoError(t, err)
+
+ testhash := header.Hash()
err = state.Block.SetFinalizedHash(testhash, 77, 1)
require.NoError(t, err)
diff --git a/dot/rpc/modules/dev_test.go b/dot/rpc/modules/dev_test.go
index 258cdd2e54..d018587658 100644
--- a/dot/rpc/modules/dev_test.go
+++ b/dot/rpc/modules/dev_test.go
@@ -7,6 +7,7 @@ import (
"github.com/ChainSafe/gossamer/dot/state"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/babe"
+ babemocks "github.com/ChainSafe/gossamer/lib/babe/mocks"
"github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/crypto/sr25519"
"github.com/ChainSafe/gossamer/lib/keystore"
@@ -47,11 +48,12 @@ func newBABEService(t *testing.T) *babe.Service {
tt.Put(common.MustHexToBytes("0x886726f904d8372fdabb7707870c2fad"), common.MustHexToBytes("0x24d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01000000000000008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48010000000000000090b5ab205c6974c9ea841be688864633dc9ca8a357843eeacf2314649965fe220100000000000000306721211d5404bd9da88e0204360a1a9ab8b87c66c1bc2fcdd37f3c2222cc200100000000000000e659a7a1628cdd93febc04a4e0646ea20e9f5f0ce097d9a05290d4a9e054df4e01000000000000001cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c01000000000000004603307f855321776922daeea21ee31720388d097cdaac66f05a6f8462b317570100000000000000be1d9d59de1283380100550a7b024501cb62d6cc40e3db35fcc5cf341814986e01000000000000001206960f920a23f7f4c43cc9081ec2ed0721f31a9bef2c10fd7602e16e08a32c0100000000000000"))
cfg := &babe.ServiceConfig{
- BlockState: bs,
- EpochState: es,
- Keypair: kr.Alice().(*sr25519.Keypair),
- Runtime: rt,
- IsDev: true,
+ BlockState: bs,
+ EpochState: es,
+ Keypair: kr.Alice().(*sr25519.Keypair),
+ Runtime: rt,
+ IsDev: true,
+ BlockImportHandler: new(babemocks.BlockImportHandler),
}
babe, err := babe.NewService(cfg)
diff --git a/dot/rpc/subscription/websocket.go b/dot/rpc/subscription/websocket.go
index 911c2aba7c..ab1b367449 100644
--- a/dot/rpc/subscription/websocket.go
+++ b/dot/rpc/subscription/websocket.go
@@ -347,9 +347,7 @@ func (c *WSConn) initExtrinsicWatch(reqID float64, params interface{}) (uint, er
// TODO (ed) since HandleSubmittedExtrinsic has been called we assume the extrinsic is in the tx queue
// should we add a channel to tx queue so we're notified when it's in the queue (See issue #1535)
- if c.CoreAPI.IsBlockProducer() {
- c.safeSend(newSubscriptionResponse(AuthorExtrinsicUpdates, esl.subID, "ready"))
- }
+ c.safeSend(newSubscriptionResponse(AuthorExtrinsicUpdates, esl.subID, "ready"))
// todo (ed) determine which peer extrinsic has been broadcast to, and set status
return esl.subID, err
diff --git a/dot/services.go b/dot/services.go
index 6b7c5697b1..fcd6c48073 100644
--- a/dot/services.go
+++ b/dot/services.go
@@ -23,6 +23,7 @@ import (
"github.com/ChainSafe/chaindb"
"github.com/ChainSafe/gossamer/dot/core"
+ "github.com/ChainSafe/gossamer/dot/digest"
"github.com/ChainSafe/gossamer/dot/network"
"github.com/ChainSafe/gossamer/dot/rpc"
"github.com/ChainSafe/gossamer/dot/rpc/modules"
@@ -185,7 +186,7 @@ func createRuntime(cfg *Config, st *state.Service, ks *keystore.GlobalKeystore,
return rt, nil
}
-func createBABEService(cfg *Config, rt runtime.Instance, st *state.Service, ks keystore.Keystore) (*babe.Service, error) {
+func createBABEService(cfg *Config, rt runtime.Instance, st *state.Service, ks keystore.Keystore, cs *core.Service) (*babe.Service, error) {
logger.Info(
"creating BABE service...",
"authority", cfg.Core.BabeAuthority,
@@ -202,16 +203,17 @@ func createBABEService(cfg *Config, rt runtime.Instance, st *state.Service, ks k
}
bcfg := &babe.ServiceConfig{
- LogLvl: cfg.Log.BlockProducerLvl,
- Runtime: rt,
- BlockState: st.Block,
- StorageState: st.Storage,
- TransactionState: st.Transaction,
- EpochState: st.Epoch,
- EpochLength: cfg.Core.EpochLength,
- SlotDuration: cfg.Core.SlotDuration, // TODO: remove this, should only be modified via runtime constant
- Authority: cfg.Core.BabeAuthority,
- IsDev: cfg.Global.ID == "dev",
+ LogLvl: cfg.Log.BlockProducerLvl,
+ Runtime: rt,
+ BlockState: st.Block,
+ StorageState: st.Storage,
+ TransactionState: st.Transaction,
+ EpochState: st.Epoch,
+ BlockImportHandler: cs,
+ EpochLength: cfg.Core.EpochLength,
+ SlotDuration: cfg.Core.SlotDuration, // TODO: remove this, should only be modified via runtime constant
+ Authority: cfg.Core.BabeAuthority,
+ IsDev: cfg.Global.ID == "dev",
}
if cfg.Core.BabeAuthority {
@@ -231,25 +233,35 @@ func createBABEService(cfg *Config, rt runtime.Instance, st *state.Service, ks k
// Core Service
// createCoreService creates the core service from the provided core configuration
-func createCoreService(cfg *Config, bp core.BlockProducer, verifier *babe.VerificationManager, rt runtime.Instance, ks *keystore.GlobalKeystore, stateSrvc *state.Service, net *network.Service) (*core.Service, error) {
+func createCoreService(cfg *Config, rt runtime.Instance, ks *keystore.GlobalKeystore, st *state.Service, net *network.Service, dh *digest.Handler) (*core.Service, error) {
logger.Debug(
"creating core service...",
"authority", cfg.Core.Roles == types.AuthorityRole,
)
+ genesisData, err := st.Base.LoadGenesisData()
+ if err != nil {
+ return nil, err
+ }
+
+ codeSubs := make(map[common.Hash]string)
+ for k, v := range genesisData.CodeSubstitutes {
+ codeSubs[common.MustHexToHash(k)] = v
+ }
+
// set core configuration
coreConfig := &core.Config{
- LogLvl: cfg.Log.CoreLvl,
- BlockState: stateSrvc.Block,
- EpochState: stateSrvc.Epoch,
- StorageState: stateSrvc.Storage,
- TransactionState: stateSrvc.Transaction,
- BlockProducer: bp,
- Keystore: ks,
- Runtime: rt,
- IsBlockProducer: cfg.Core.BabeAuthority,
- Verifier: verifier,
- Network: net,
+ LogLvl: cfg.Log.CoreLvl,
+ BlockState: st.Block,
+ EpochState: st.Epoch,
+ StorageState: st.Storage,
+ TransactionState: st.Transaction,
+ Keystore: ks,
+ Runtime: rt,
+ Network: net,
+ DigestHandler: dh,
+ CodeSubstitutes: codeSubs,
+ CodeSubstitutedState: st.Base,
}
// create new core service
@@ -353,7 +365,7 @@ func createSystemService(cfg *types.SystemInfo, stateSrvc *state.Service) (*syst
}
// createGRANDPAService creates a new GRANDPA service
-func createGRANDPAService(cfg *Config, rt runtime.Instance, st *state.Service, dh *core.DigestHandler, ks keystore.Keystore, net *network.Service) (*grandpa.Service, error) {
+func createGRANDPAService(cfg *Config, rt runtime.Instance, st *state.Service, dh *digest.Handler, ks keystore.Keystore, net *network.Service) (*grandpa.Service, error) {
ad, err := rt.GrandpaAuthorities()
if err != nil {
return nil, err
@@ -396,32 +408,21 @@ func createBlockVerifier(st *state.Service) (*babe.VerificationManager, error) {
return ver, nil
}
-func newSyncService(cfg *Config, st *state.Service, bp sync.BlockProducer, fg sync.FinalityGadget, dh *core.DigestHandler, verifier *babe.VerificationManager, rt runtime.Instance) (*sync.Service, error) {
- genesisData, err := st.Base.LoadGenesisData()
- if err != nil {
- return nil, err
- }
- codeSubs := make(map[common.Hash]string)
- for k, v := range genesisData.CodeSubstitutes {
- codeSubs[common.MustHexToHash(k)] = v
- }
+func newSyncService(cfg *Config, st *state.Service, fg sync.FinalityGadget, verifier *babe.VerificationManager, rt runtime.Instance, cs *core.Service) (*sync.Service, error) {
syncCfg := &sync.Config{
- LogLvl: cfg.Log.SyncLvl,
- BlockState: st.Block,
- StorageState: st.Storage,
- TransactionState: st.Transaction,
- BlockProducer: bp,
- FinalityGadget: fg,
- Verifier: verifier,
- Runtime: rt,
- DigestHandler: dh,
- CodeSubstitutes: codeSubs,
- CodeSubstitutedState: st.Base,
+ LogLvl: cfg.Log.SyncLvl,
+ BlockState: st.Block,
+ StorageState: st.Storage,
+ TransactionState: st.Transaction,
+ FinalityGadget: fg,
+ Verifier: verifier,
+ Runtime: rt,
+ BlockImportHandler: cs,
}
return sync.NewService(syncCfg)
}
-func createDigestHandler(st *state.Service, bp core.BlockProducer, verifier *babe.VerificationManager) (*core.DigestHandler, error) {
- return core.NewDigestHandler(st.Block, st.Epoch, st.Grandpa, bp, verifier)
+func createDigestHandler(st *state.Service) (*digest.Handler, error) {
+ return digest.NewHandler(st.Block, st.Epoch, st.Grandpa)
}
diff --git a/dot/services_test.go b/dot/services_test.go
index adb9a7ea36..67ed1d4638 100644
--- a/dot/services_test.go
+++ b/dot/services_test.go
@@ -23,7 +23,6 @@ import (
"time"
"github.com/ChainSafe/gossamer/dot/network"
- "github.com/ChainSafe/gossamer/dot/sync"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/keystore"
"github.com/ChainSafe/gossamer/lib/utils"
@@ -86,8 +85,11 @@ func TestCreateCoreService(t *testing.T) {
rt, err := createRuntime(cfg, stateSrvc, ks, networkSrvc)
require.NoError(t, err)
- coreSrvc, err := createCoreService(cfg, nil, nil, rt, ks, stateSrvc, networkSrvc)
- require.Nil(t, err)
+ dh, err := createDigestHandler(stateSrvc)
+ require.NoError(t, err)
+
+ coreSrvc, err := createCoreService(cfg, rt, ks, stateSrvc, networkSrvc, dh)
+ require.NoError(t, err)
require.NotNil(t, coreSrvc)
}
@@ -103,7 +105,7 @@ func TestCreateBlockVerifier(t *testing.T) {
cfg.Init.Genesis = genFile.Name()
err := InitNode(cfg)
- require.Nil(t, err)
+ require.NoError(t, err)
stateSrvc, err := createStateService(cfg)
require.NoError(t, err)
@@ -124,7 +126,7 @@ func TestCreateSyncService(t *testing.T) {
cfg.Init.Genesis = genFile.Name()
err := InitNode(cfg)
- require.Nil(t, err)
+ require.NoError(t, err)
stateSrvc, err := createStateService(cfg)
require.NoError(t, err)
@@ -137,7 +139,13 @@ func TestCreateSyncService(t *testing.T) {
ver, err := createBlockVerifier(stateSrvc)
require.NoError(t, err)
- _, err = newSyncService(cfg, stateSrvc, sync.NewMockBlockProducer(), nil, nil, ver, rt)
+ dh, err := createDigestHandler(stateSrvc)
+ require.NoError(t, err)
+
+ coreSrvc, err := createCoreService(cfg, rt, ks, stateSrvc, &network.Service{}, dh)
+ require.NoError(t, err)
+
+ _, err = newSyncService(cfg, stateSrvc, nil, ver, rt, coreSrvc)
require.NoError(t, err)
}
@@ -154,13 +162,13 @@ func TestCreateNetworkService(t *testing.T) {
cfg.Init.Genesis = genFile.Name()
err := InitNode(cfg)
- require.Nil(t, err)
+ require.NoError(t, err)
stateSrvc, err := createStateService(cfg)
- require.Nil(t, err)
+ require.NoError(t, err)
networkSrvc, err := createNetworkService(cfg, stateSrvc)
- require.Nil(t, err)
+ require.NoError(t, err)
// TODO: improve dot tests #687
require.NotNil(t, networkSrvc)
@@ -183,10 +191,10 @@ func TestCreateRPCService(t *testing.T) {
cfg.Init.Genesis = genFile.Name()
err := InitNode(cfg)
- require.Nil(t, err)
+ require.NoError(t, err)
stateSrvc, err := createStateService(cfg)
- require.Nil(t, err)
+ require.NoError(t, err)
networkSrvc := &network.Service{}
@@ -197,8 +205,11 @@ func TestCreateRPCService(t *testing.T) {
rt, err := createRuntime(cfg, stateSrvc, ks, networkSrvc)
require.NoError(t, err)
- coreSrvc, err := createCoreService(cfg, nil, nil, rt, ks, stateSrvc, networkSrvc)
- require.Nil(t, err)
+ dh, err := createDigestHandler(stateSrvc)
+ require.NoError(t, err)
+
+ coreSrvc, err := createCoreService(cfg, rt, ks, stateSrvc, networkSrvc, dh)
+ require.NoError(t, err)
sysSrvc, err := createSystemService(&cfg.System, stateSrvc)
require.NoError(t, err)
@@ -221,20 +232,26 @@ func TestCreateBABEService(t *testing.T) {
cfg.Init.Genesis = genFile.Name()
err := InitNode(cfg)
- require.Nil(t, err)
+ require.NoError(t, err)
stateSrvc, err := createStateService(cfg)
- require.Nil(t, err)
+ require.NoError(t, err)
ks := keystore.NewGlobalKeystore()
kr, err := keystore.NewSr25519Keyring()
- require.Nil(t, err)
+ require.NoError(t, err)
ks.Babe.Insert(kr.Alice())
rt, err := createRuntime(cfg, stateSrvc, ks, &network.Service{})
require.NoError(t, err)
- bs, err := createBABEService(cfg, rt, stateSrvc, ks.Babe)
+ dh, err := createDigestHandler(stateSrvc)
+ require.NoError(t, err)
+
+ coreSrvc, err := createCoreService(cfg, rt, ks, stateSrvc, &network.Service{}, dh)
+ require.NoError(t, err)
+
+ bs, err := createBABEService(cfg, rt, stateSrvc, ks.Babe, coreSrvc)
require.NoError(t, err)
require.NotNil(t, bs)
}
@@ -266,7 +283,7 @@ func TestCreateGrandpaService(t *testing.T) {
rt, err := createRuntime(cfg, stateSrvc, ks, &network.Service{})
require.NoError(t, err)
- dh, err := createDigestHandler(stateSrvc, nil, nil)
+ dh, err := createDigestHandler(stateSrvc)
require.NoError(t, err)
gs, err := createGRANDPAService(cfg, rt, stateSrvc, dh, ks.Gran, &network.Service{})
@@ -318,7 +335,10 @@ func TestNewWebSocketServer(t *testing.T) {
rt, err := createRuntime(cfg, stateSrvc, ks, networkSrvc)
require.NoError(t, err)
- coreSrvc, err := createCoreService(cfg, nil, nil, rt, ks, stateSrvc, networkSrvc)
+ dh, err := createDigestHandler(stateSrvc)
+ require.NoError(t, err)
+
+ coreSrvc, err := createCoreService(cfg, rt, ks, stateSrvc, networkSrvc, dh)
require.Nil(t, err)
sysSrvc, err := createSystemService(&cfg.System, stateSrvc)
diff --git a/dot/state/block.go b/dot/state/block.go
index 4ff653f9ce..82b7aeab49 100644
--- a/dot/state/block.go
+++ b/dot/state/block.go
@@ -402,6 +402,11 @@ func (bs *BlockState) SetFinalizedHash(hash common.Hash, round, setID uint64) er
bs.Lock()
defer bs.Unlock()
+ has, _ := bs.HasHeader(hash)
+ if !has {
+ return fmt.Errorf("cannot finalise unknown block %s", hash)
+ }
+
go bs.notifyFinalized(hash, round, setID)
if round > 0 {
err := bs.SetRound(round)
diff --git a/dot/state/block_test.go b/dot/state/block_test.go
index fc54bbceea..0f19f5c224 100644
--- a/dot/state/block_test.go
+++ b/dot/state/block_test.go
@@ -274,6 +274,9 @@ func TestFinalizedHash(t *testing.T) {
require.Equal(t, testGenesisHeader.Hash(), h)
testhash := common.Hash{1, 2, 3, 4}
+ err = bs.db.Put(headerKey(testhash), []byte{})
+ require.NoError(t, err)
+
err = bs.SetFinalizedHash(testhash, 1, 1)
require.NoError(t, err)
diff --git a/dot/sync/errors.go b/dot/sync/errors.go
index 708b9ee3f4..89d33e03cb 100644
--- a/dot/sync/errors.go
+++ b/dot/sync/errors.go
@@ -21,32 +21,25 @@ import (
"fmt"
)
-// ErrNilBlockState is returned when BlockState is nil
-var ErrNilBlockState = errors.New("cannot have nil BlockState")
+var (
+ errNilBlockState = errors.New("cannot have nil BlockState")
+ errNilStorageState = errors.New("cannot have nil StorageState")
+ errNilVerifier = errors.New("cannot have nil Verifier")
+ errNilRuntime = errors.New("cannot have nil runtime")
+ errNilBlockImportHandler = errors.New("cannot have nil BlockImportHandler")
-// ErrNilStorageState is returned when StorageState is nil
-var ErrNilStorageState = errors.New("cannot have nil StorageState")
+ // ErrNilBlockData is returned when trying to process a BlockResponseMessage with nil BlockData
+ ErrNilBlockData = errors.New("got nil BlockData")
-// ErrNilVerifier is returned when trying to instantiate a Syncer without a Verifier
-var ErrNilVerifier = errors.New("cannot have nil Verifier")
+ // ErrServiceStopped is returned when the service has been stopped
+ ErrServiceStopped = errors.New("service has been stopped")
-// ErrNilRuntime is returned when trying to instantiate a Service or Syncer without a runtime
-var ErrNilRuntime = errors.New("cannot have nil runtime")
+ // ErrInvalidBlock is returned when a block cannot be verified
+ ErrInvalidBlock = errors.New("could not verify block")
-// ErrNilBlockData is returned when trying to process a BlockResponseMessage with nil BlockData
-var ErrNilBlockData = errors.New("got nil BlockData")
-
-// ErrServiceStopped is returned when the service has been stopped
-var ErrServiceStopped = errors.New("service has been stopped")
-
-// ErrInvalidBlock is returned when a block cannot be verified
-var ErrInvalidBlock = errors.New("could not verify block")
-
-// ErrInvalidBlockRequest is returned when an invalid block request is received
-var ErrInvalidBlockRequest = errors.New("invalid block request")
-
-// ErrEmptyRuntimeCode is returned when the storage :code is empty
-var ErrEmptyRuntimeCode = errors.New("new :code is empty")
+ // ErrInvalidBlockRequest is returned when an invalid block request is received
+ ErrInvalidBlockRequest = errors.New("invalid block request")
+)
// ErrNilChannel is returned if a channel is nil
func ErrNilChannel(s string) error {
diff --git a/dot/sync/interface.go b/dot/sync/interface.go
index f88bbd11cd..f294d9fe09 100644
--- a/dot/sync/interface.go
+++ b/dot/sync/interface.go
@@ -21,7 +21,6 @@ import (
"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"
)
@@ -46,6 +45,7 @@ type BlockState interface {
SetFinalizedHash(hash common.Hash, round, setID uint64) error
AddBlockToBlockTree(header *types.Header) error
GetHashByNumber(*big.Int) (common.Hash, error)
+ GetBlockByHash(common.Hash) (*types.Block, error)
}
// StorageState is the interface for the storage state
@@ -67,18 +67,6 @@ type TransactionState interface {
RemoveExtrinsic(ext types.Extrinsic)
}
-// BlockProducer is the interface that a block production service must implement
-type BlockProducer interface {
- Pause() error
- Resume() error
- SetRuntime(rt runtime.Instance)
-}
-
-// DigestHandler is the interface for the consensus digest handler
-type DigestHandler interface {
- HandleConsensusDigest(*types.ConsensusDigest, *types.Header) error
-}
-
// Verifier deals with block verification
type Verifier interface {
VerifyBlock(header *types.Header) error
@@ -88,3 +76,8 @@ type Verifier interface {
type FinalityGadget interface {
VerifyBlockJustification([]byte) error
}
+
+// BlockImportHandler is the interface for the handler of newly imported blocks
+type BlockImportHandler interface {
+ HandleBlockImport(block *types.Block, state *rtstorage.TrieState) error
+}
diff --git a/dot/sync/mocks/block_import_handler.go b/dot/sync/mocks/block_import_handler.go
new file mode 100644
index 0000000000..8da0026722
--- /dev/null
+++ b/dot/sync/mocks/block_import_handler.go
@@ -0,0 +1,29 @@
+// Code generated by mockery v2.8.0. DO NOT EDIT.
+
+package sync
+
+import (
+ storage "github.com/ChainSafe/gossamer/lib/runtime/storage"
+ mock "github.com/stretchr/testify/mock"
+
+ types "github.com/ChainSafe/gossamer/dot/types"
+)
+
+// MockBlockImportHandler is an autogenerated mock type for the BlockImportHandler type
+type MockBlockImportHandler struct {
+ mock.Mock
+}
+
+// HandleBlockImport provides a mock function with given fields: block, state
+func (_m *MockBlockImportHandler) HandleBlockImport(block *types.Block, state *storage.TrieState) error {
+ ret := _m.Called(block, state)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(*types.Block, *storage.TrieState) error); ok {
+ r0 = rf(block, state)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
diff --git a/dot/sync/mocks/block_producer.go b/dot/sync/mocks/block_producer.go
deleted file mode 100644
index 7d4d62556f..0000000000
--- a/dot/sync/mocks/block_producer.go
+++ /dev/null
@@ -1,46 +0,0 @@
-// Code generated by mockery v2.8.0. DO NOT EDIT.
-
-package sync
-
-import (
- runtime "github.com/ChainSafe/gossamer/lib/runtime"
- mock "github.com/stretchr/testify/mock"
-)
-
-// MockBlockProducer is an autogenerated mock type for the BlockProducer type
-type MockBlockProducer struct {
- mock.Mock
-}
-
-// Pause provides a mock function with given fields:
-func (_m *MockBlockProducer) Pause() error {
- ret := _m.Called()
-
- var r0 error
- if rf, ok := ret.Get(0).(func() error); ok {
- r0 = rf()
- } else {
- r0 = ret.Error(0)
- }
-
- return r0
-}
-
-// Resume provides a mock function with given fields:
-func (_m *MockBlockProducer) Resume() error {
- ret := _m.Called()
-
- var r0 error
- if rf, ok := ret.Get(0).(func() error); ok {
- r0 = rf()
- } else {
- r0 = ret.Error(0)
- }
-
- return r0
-}
-
-// SetRuntime provides a mock function with given fields: rt
-func (_m *MockBlockProducer) SetRuntime(rt runtime.Instance) {
- _m.Called(rt)
-}
diff --git a/dot/sync/syncer.go b/dot/sync/syncer.go
index d67ef0327e..c6e3b5cf5b 100644
--- a/dot/sync/syncer.go
+++ b/dot/sync/syncer.go
@@ -27,9 +27,8 @@ import (
"github.com/ChainSafe/gossamer/dot/telemetry"
"github.com/ChainSafe/gossamer/dot/types"
"github.com/ChainSafe/gossamer/lib/blocktree"
- "github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/runtime"
- rtstorage "github.com/ChainSafe/gossamer/lib/runtime/storage"
+
log "github.com/ChainSafe/log15"
)
@@ -37,14 +36,12 @@ var logger = log.New("pkg", "sync")
// Service deals with chain syncing by sending block request messages and watching for responses.
type Service struct {
- codeHash common.Hash // cached hash of runtime code
-
// State interfaces
- blockState BlockState // retrieve our current head of chain from BlockState
- storageState StorageState
- transactionState TransactionState
- blockProducer BlockProducer
- finalityGadget FinalityGadget
+ blockState BlockState // retrieve our current head of chain from BlockState
+ storageState StorageState
+ transactionState TransactionState
+ finalityGadget FinalityGadget
+ blockImportHandler BlockImportHandler
// Synchronisation variables
synced bool
@@ -53,75 +50,56 @@ type Service struct {
// BABE verification
verifier Verifier
-
- // Consensus digest handling
- digestHandler DigestHandler
-
- // map of code substitutions keyed by block hash
- codeSubstitute map[common.Hash]string
- codeSubstitutedState CodeSubstitutedState
}
// Config is the configuration for the sync Service.
type Config struct {
- LogLvl log.Lvl
- BlockState BlockState
- StorageState StorageState
- BlockProducer BlockProducer
- FinalityGadget FinalityGadget
- TransactionState TransactionState
- Runtime runtime.Instance
- Verifier Verifier
- DigestHandler DigestHandler
- CodeSubstitutes map[common.Hash]string
- CodeSubstitutedState CodeSubstitutedState
+ LogLvl log.Lvl
+ BlockState BlockState
+ StorageState StorageState
+ FinalityGadget FinalityGadget
+ TransactionState TransactionState
+ BlockImportHandler BlockImportHandler
+ Runtime runtime.Instance
+ Verifier Verifier
}
// NewService returns a new *sync.Service
func NewService(cfg *Config) (*Service, error) {
if cfg.BlockState == nil {
- return nil, ErrNilBlockState
+ return nil, errNilBlockState
}
if cfg.StorageState == nil {
- return nil, ErrNilStorageState
+ return nil, errNilStorageState
}
if cfg.Verifier == nil {
- return nil, ErrNilVerifier
+ return nil, errNilVerifier
}
if cfg.Runtime == nil {
- return nil, ErrNilRuntime
+ return nil, errNilRuntime
}
- if cfg.BlockProducer == nil {
- cfg.BlockProducer = NewMockBlockProducer()
+ if cfg.BlockImportHandler == nil {
+ return nil, errNilBlockImportHandler
}
handler := log.StreamHandler(os.Stdout, log.TerminalFormat())
handler = log.CallerFileHandler(handler)
logger.SetHandler(log.LvlFilterHandler(cfg.LogLvl, handler))
- codeHash, err := cfg.StorageState.LoadCodeHash(nil)
- if err != nil {
- return nil, err
- }
-
return &Service{
- codeHash: codeHash,
- blockState: cfg.BlockState,
- storageState: cfg.StorageState,
- blockProducer: cfg.BlockProducer,
- finalityGadget: cfg.FinalityGadget,
- synced: true,
- highestSeenBlock: big.NewInt(0),
- transactionState: cfg.TransactionState,
- runtime: cfg.Runtime,
- verifier: cfg.Verifier,
- digestHandler: cfg.DigestHandler,
- codeSubstitute: cfg.CodeSubstitutes,
- codeSubstitutedState: cfg.CodeSubstitutedState,
+ blockState: cfg.BlockState,
+ storageState: cfg.StorageState,
+ finalityGadget: cfg.FinalityGadget,
+ blockImportHandler: cfg.BlockImportHandler,
+ synced: true,
+ highestSeenBlock: big.NewInt(0),
+ transactionState: cfg.TransactionState,
+ runtime: cfg.Runtime,
+ verifier: cfg.Verifier,
}, nil
}
@@ -203,33 +181,36 @@ func (s *Service) ProcessBlockData(data []*types.BlockData) (int, error) {
// so when the node restarts it has blocks higher than what it thinks is the best, causing it not to sync
logger.Debug("skipping block, already have", "hash", bd.Hash)
- header, err := s.blockState.GetHeader(bd.Hash) //nolint
+ block, err := s.blockState.GetBlockByHash(bd.Hash) //nolint
if err != nil {
logger.Debug("failed to get header", "hash", bd.Hash, "error", err)
return i, err
}
- err = s.blockState.AddBlockToBlockTree(header)
+ err = s.blockState.AddBlockToBlockTree(block.Header)
if err != nil && !errors.Is(err, blocktree.ErrBlockExists) {
logger.Warn("failed to add block to blocktree", "hash", bd.Hash, "error", err)
return i, err
}
- // handle consensus digests for authority changes
- if s.digestHandler != nil {
- s.handleDigests(header)
- }
-
if bd.Justification != nil && bd.Justification.Exists() {
- logger.Debug("handling Justification...", "number", header.Number, "hash", bd.Hash)
- s.handleJustification(header, bd.Justification.Value())
+ logger.Debug("handling Justification...", "number", block.Header.Number, "hash", bd.Hash)
+ s.handleJustification(block.Header, bd.Justification.Value())
}
- if err := s.handleCodeSubstitution(bd.Hash); err != nil {
- logger.Warn("failed to handle code substitution", "error", err)
+ // TODO: this is probably unnecessary, since the state is already in the database
+ // however, this case shouldn't be hit often, since it's only hit if the node state
+ // is rewinded or if the node shuts down unexpectedly
+ state, err := s.storageState.TrieState(&block.Header.StateRoot)
+ if err != nil {
+ logger.Warn("failed to load state for block", "block", block.Header.Hash(), "error", err)
return i, err
}
+ if err := s.blockImportHandler.HandleBlockImport(block, state); err != nil {
+ logger.Warn("failed to handle block import", "error", err)
+ }
+
continue
}
@@ -359,45 +340,22 @@ func (s *Service) handleBlock(block *types.Block) error {
return fmt.Errorf("failed to execute block %d: %w", block.Header.Number, err)
}
- err = s.storageState.StoreTrie(ts, block.Header)
- if err != nil {
+ if err = s.blockImportHandler.HandleBlockImport(block, ts); err != nil {
return err
}
- logger.Trace("executed block and stored resulting state", "state root", ts.MustRoot())
-
- // TODO: batch writes in AddBlock
- err = s.blockState.AddBlock(block)
- if 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 {
- // this is fine
- } else {
- return err
- }
- } else {
- logger.Debug("π imported block", "number", block.Header.Number, "hash", block.Header.Hash())
- err := telemetry.GetInstance().SendMessage(telemetry.NewTelemetryMessage( // nolint
- telemetry.NewKeyValue("best", block.Header.Hash().String()),
- telemetry.NewKeyValue("height", block.Header.Number.Uint64()),
- telemetry.NewKeyValue("msg", "block.import"),
- telemetry.NewKeyValue("origin", "NetworkInitialSync")))
- if err != nil {
- logger.Debug("problem sending block.import telemetry message", "error", err)
- }
- }
- // handle consensus digest for authority changes
- if s.digestHandler != nil {
- s.handleDigests(block.Header)
- }
+ logger.Debug("π imported block", "number", block.Header.Number, "hash", block.Header.Hash())
- err = s.handleCodeSubstitution(block.Header.Hash())
+ err = telemetry.GetInstance().SendMessage(telemetry.NewTelemetryMessage( // nolint
+ telemetry.NewKeyValue("best", block.Header.Hash().String()),
+ telemetry.NewKeyValue("height", block.Header.Number.Uint64()),
+ telemetry.NewKeyValue("msg", "block.import"),
+ telemetry.NewKeyValue("origin", "NetworkInitialSync")))
if err != nil {
- return err
+ logger.Trace("problem sending block.import telemetry message", "error", err)
}
- return s.handleRuntimeChanges(ts)
+ return nil
}
func (s *Service) handleJustification(header *types.Header, justification []byte) {
@@ -426,103 +384,6 @@ func (s *Service) handleJustification(header *types.Header, justification []byte
logger.Info("π¨ finalised block", "number", header.Number, "hash", header.Hash())
}
-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.runtime.CheckRuntimeVersion(code) // nolint
- if err != nil {
- logger.Debug("problem checking runtime version", "error", err)
- return err
- }
-
- previousVersion, _ := s.runtime.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.runtime.UpdateRuntimeCode(code)
- if err != nil {
- logger.Crit("failed to update runtime code", "error", err)
- return err
- }
-
- s.codeHash = currCodeHash
-
- err = s.codeSubstitutedState.StoreCodeSubstitutedBlockHash(common.Hash{})
- if err != nil {
- logger.Error("failed to update code substituted block hash", "error", err)
- return err
- }
-
- return nil
-}
-
-func (s *Service) handleCodeSubstitution(hash common.Hash) error {
- value := s.codeSubstitute[hash]
- if value == "" {
- return nil
- }
-
- logger.Info("π detected runtime code substitution, upgrading...", "block", hash)
- code := common.MustHexToBytes(value)
- if len(code) == 0 {
- return ErrEmptyRuntimeCode
- }
-
- err := s.runtime.UpdateRuntimeCode(code)
- if err != nil {
- logger.Crit("failed to substitute runtime code", "error", err)
- return err
- }
-
- err = s.codeSubstitutedState.StoreCodeSubstitutedBlockHash(hash)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-func (s *Service) handleDigests(header *types.Header) {
- for i, d := range header.Digest {
- if d.Type() == types.ConsensusDigestType {
- cd, ok := d.(*types.ConsensusDigest)
- if !ok {
- logger.Error("handleDigests", "block number", header.Number, "index", i, "error", "cannot cast invalid consensus digest item")
- continue
- }
-
- err := s.digestHandler.HandleConsensusDigest(cd, header)
- if err != nil {
- logger.Error("handleDigests", "block number", header.Number, "index", i, "digest", cd, "error", err)
- }
- }
- }
-}
-
// IsSynced exposes the synced state
func (s *Service) IsSynced() bool {
return s.synced
diff --git a/dot/sync/syncer_test.go b/dot/sync/syncer_test.go
index 150f39df4d..12611722af 100644
--- a/dot/sync/syncer_test.go
+++ b/dot/sync/syncer_test.go
@@ -18,7 +18,6 @@ package sync
import (
"errors"
- "io/ioutil"
"math/big"
"os"
"testing"
@@ -27,7 +26,6 @@ import (
"github.com/ChainSafe/gossamer/dot/network"
"github.com/ChainSafe/gossamer/dot/state"
"github.com/ChainSafe/gossamer/dot/types"
- "github.com/ChainSafe/gossamer/lib/common"
"github.com/ChainSafe/gossamer/lib/common/optional"
"github.com/ChainSafe/gossamer/lib/common/variadic"
"github.com/ChainSafe/gossamer/lib/runtime"
@@ -213,59 +211,6 @@ func TestSyncer_ExecuteBlock(t *testing.T) {
require.NoError(t, err)
}
-func TestSyncer_HandleRuntimeChanges(t *testing.T) {
- syncer := NewTestSyncer(t, false)
- codeHashBefore := syncer.codeHash
-
- testRuntime, err := ioutil.ReadFile(runtime.POLKADOT_RUNTIME_FP)
- require.NoError(t, err)
-
- ts, err := syncer.storageState.TrieState(nil)
- require.NoError(t, err)
-
- ts.Set(common.CodeKey, testRuntime)
- err = syncer.handleRuntimeChanges(ts)
- require.NoError(t, err)
- codeHashAfter := syncer.codeHash
- require.NotEqualf(t, codeHashBefore, codeHashAfter, "expected different code hash after runtime update")
-}
-
-func TestSyncer_HandleCodeSubstitutes(t *testing.T) {
- syncer := NewTestSyncer(t, true)
- nonSubBlockHash := common.MustHexToHash("0x86aa36a140dfc449c30dbce16ce0fea33d5c3786766baa764e33f336841b9e28")
- err := syncer.handleCodeSubstitution(nonSubBlockHash)
- require.NoError(t, err)
- codSub := syncer.codeSubstitutedState.LoadCodeSubstitutedBlockHash()
- require.Equal(t, common.Hash{}, codSub)
-
- subBlockHash := common.MustHexToHash("0x86aa36a140dfc449c30dbce16ce0fea33d5c3786766baa764e33f336841b9e29") // hash for known test code substitution
- err = syncer.handleCodeSubstitution(subBlockHash)
- require.NoError(t, err)
- codSub = syncer.codeSubstitutedState.LoadCodeSubstitutedBlockHash()
- require.Equal(t, subBlockHash, codSub)
-}
-
-func TestSyncer_HandleRuntimeChangesAfterCodeSubstitutes(t *testing.T) {
- syncer := NewTestSyncer(t, true)
- codeHashBefore := syncer.codeHash
- blockHash := common.MustHexToHash("0x86aa36a140dfc449c30dbce16ce0fea33d5c3786766baa764e33f336841b9e29") // hash for known test code substitution
-
- err := syncer.handleCodeSubstitution(blockHash)
- require.NoError(t, err)
- require.Equal(t, codeHashBefore, syncer.codeHash) // codeHash should remain unchanged after code substitute
-
- testRuntime, err := ioutil.ReadFile(runtime.POLKADOT_RUNTIME_FP)
- require.NoError(t, err)
-
- ts, err := syncer.storageState.TrieState(nil)
- require.NoError(t, err)
-
- ts.Set(common.CodeKey, testRuntime)
- err = syncer.handleRuntimeChanges(ts)
- require.NoError(t, err)
- require.NotEqualf(t, codeHashBefore, syncer.codeHash, "expected different code hash after runtime update") // codeHash should change after runtime change
-}
-
func TestSyncer_HandleJustification(t *testing.T) {
syncer := NewTestSyncer(t, false)
@@ -275,6 +220,9 @@ func TestSyncer_HandleJustification(t *testing.T) {
just := []byte("testjustification")
+ err := syncer.blockState.SetHeader(header)
+ require.NoError(t, err)
+
syncer.handleJustification(header, just)
res, err := syncer.blockState.GetJustification(header.Hash())
diff --git a/dot/sync/test_helpers.go b/dot/sync/test_helpers.go
index ae2e43c80c..0059d65b10 100644
--- a/dot/sync/test_helpers.go
+++ b/dot/sync/test_helpers.go
@@ -56,16 +56,6 @@ func NewMockVerifier() *syncmocks.MockVerifier {
return m
}
-// NewMockBlockProducer create and return sync BlockProducer interface mock
-func NewMockBlockProducer() *syncmocks.MockBlockProducer {
- m := new(syncmocks.MockBlockProducer)
- m.On("Pause").Return(nil)
- m.On("Resume").Return(nil)
- m.On("SetRuntime", mock.AnythingOfType("runtime.Instance"))
-
- return m
-}
-
// NewTestSyncer ...
func NewTestSyncer(t *testing.T, usePolkadotGenesis bool) *Service {
wasmer.DefaultTestLogLvl = 3
@@ -95,9 +85,8 @@ func NewTestSyncer(t *testing.T, usePolkadotGenesis bool) *Service {
cfg.StorageState = stateSrvc.Storage
}
- if cfg.BlockProducer == nil {
- cfg.BlockProducer = NewMockBlockProducer()
- }
+ cfg.BlockImportHandler = new(syncmocks.MockBlockImportHandler)
+ cfg.BlockImportHandler.(*syncmocks.MockBlockImportHandler).On("HandleBlockImport", mock.AnythingOfType("*types.Block"), mock.AnythingOfType("*storage.TrieState")).Return(nil)
if cfg.Runtime == nil {
// set state to genesis state
@@ -129,21 +118,6 @@ func NewTestSyncer(t *testing.T, usePolkadotGenesis bool) *Service {
cfg.FinalityGadget = NewMockFinalityGadget()
}
- if cfg.CodeSubstitutes == nil {
- cfg.CodeSubstitutes = make(map[common.Hash]string)
-
- genesisData, err := stateSrvc.Base.LoadGenesisData() // nolint
- require.NoError(t, err)
-
- for k, v := range genesisData.CodeSubstitutes {
- cfg.CodeSubstitutes[common.MustHexToHash(k)] = v
- }
- }
-
- if cfg.CodeSubstitutedState == nil {
- cfg.CodeSubstitutedState = stateSrvc.Base
- }
-
syncer, err := NewService(cfg)
require.NoError(t, err)
return syncer
diff --git a/lib/babe/babe.go b/lib/babe/babe.go
index e8f2911e18..43a798f135 100644
--- a/lib/babe/babe.go
+++ b/lib/babe/babe.go
@@ -48,6 +48,8 @@ type Service struct {
epochState EpochState
epochLength uint64
+ blockImportHandler BlockImportHandler
+
// BABE authority keypair
keypair *sr25519.Keypair // TODO: change to BABE keystore
@@ -60,9 +62,6 @@ type Service struct {
slotToProof map[uint64]*VrfOutputAndProof // for slots where we are a producer, store the vrf output (bytes 0-32) + proof (bytes 32-96)
isDisabled bool
- // Channels for inter-process communication
- blockChan chan types.Block // send blocks to core service
-
// State variables
sync.RWMutex
pause chan struct{}
@@ -75,6 +74,7 @@ type ServiceConfig struct {
StorageState StorageState
TransactionState TransactionState
EpochState EpochState
+ BlockImportHandler BlockImportHandler
Keypair *sr25519.Keypair
Runtime runtime.Instance
AuthData []*types.Authority
@@ -93,15 +93,19 @@ func NewService(cfg *ServiceConfig) (*Service, error) {
}
if cfg.BlockState == nil {
- return nil, errors.New("blockState is nil")
+ return nil, errNilBlockState
}
if cfg.EpochState == nil {
- return nil, errors.New("epochState is nil")
+ return nil, errNilEpochState
}
if cfg.Runtime == nil {
- return nil, errors.New("runtime is nil")
+ return nil, errNilRuntime
+ }
+
+ if cfg.BlockImportHandler == nil {
+ return nil, errNilBlockImportHandler
}
logger = log.New("pkg", "babe")
@@ -112,20 +116,20 @@ func NewService(cfg *ServiceConfig) (*Service, error) {
ctx, cancel := context.WithCancel(context.Background())
babeService := &Service{
- ctx: ctx,
- cancel: cancel,
- blockState: cfg.BlockState,
- storageState: cfg.StorageState,
- epochState: cfg.EpochState,
- epochLength: cfg.EpochLength,
- keypair: cfg.Keypair,
- rt: cfg.Runtime,
- transactionState: cfg.TransactionState,
- slotToProof: make(map[uint64]*VrfOutputAndProof),
- blockChan: make(chan types.Block, 16),
- pause: make(chan struct{}),
- authority: cfg.Authority,
- dev: cfg.IsDev,
+ ctx: ctx,
+ cancel: cancel,
+ blockState: cfg.BlockState,
+ storageState: cfg.StorageState,
+ epochState: cfg.EpochState,
+ epochLength: cfg.EpochLength,
+ keypair: cfg.Keypair,
+ rt: cfg.Runtime,
+ transactionState: cfg.TransactionState,
+ slotToProof: make(map[uint64]*VrfOutputAndProof),
+ pause: make(chan struct{}),
+ authority: cfg.Authority,
+ dev: cfg.IsDev,
+ blockImportHandler: cfg.BlockImportHandler,
}
genCfg, err := babeService.rt.BabeConfiguration()
@@ -269,15 +273,11 @@ func (b *Service) IsPaused() bool {
// Stop stops the service. If stop is called, it cannot be resumed.
func (b *Service) Stop() error {
- b.Lock()
- defer b.Unlock()
-
if b.ctx.Err() != nil {
return errors.New("service already stopped")
}
b.cancel()
- close(b.blockChan)
return nil
}
@@ -286,11 +286,6 @@ func (b *Service) SetRuntime(rt runtime.Instance) {
b.rt = rt
}
-// GetBlockChannel returns the channel where new blocks are passed
-func (b *Service) GetBlockChannel() <-chan types.Block {
- return b.blockChan
-}
-
// SetOnDisabled sets the block producer with the given index as disabled
// If this is our node, we stop producing blocks
func (b *Service) SetOnDisabled(authorityIndex uint32) { // TODO: remove this
@@ -309,18 +304,6 @@ func (b *Service) IsStopped() bool {
return b.ctx.Err() != nil
}
-func (b *Service) safeSend(msg types.Block) error {
- b.Lock()
- defer b.Unlock()
-
- if b.IsStopped() {
- return errors.New("service has been stopped")
- }
-
- b.blockChan <- msg
- return nil
-}
-
func (b *Service) getAuthorityIndex(Authorities []*types.Authority) (uint32, error) {
if !b.authority {
return 0, ErrNotAuthority
@@ -504,23 +487,19 @@ func (b *Service) handleSlot(slotNum uint64) error {
return err
}
- err = b.storageState.StoreTrie(ts, block.Header)
- if err != nil {
- logger.Error("failed to store trie in storage state", "error", err)
- }
-
- hash := block.Header.Hash()
- logger.Info("built block", "hash", hash.String(), "number", block.Header.Number, "state root", block.Header.StateRoot, "slot", slotNum)
- logger.Debug("built block", "header", block.Header, "body", block.Body, "parent", parent.Hash())
-
- err = b.blockState.AddBlock(block)
- if err != nil {
- return err
- }
+ logger.Info("built block", "hash", block.Header.Hash().String(),
+ "number", block.Header.Number,
+ "state root", block.Header.StateRoot,
+ "slot", slotNum,
+ )
+ logger.Debug("built block",
+ "header", block.Header,
+ "body", block.Body,
+ "parent", parent.Hash(),
+ )
- err = b.safeSend(*block)
- if err != nil {
- logger.Error("failed to send block to core", "error", err)
+ if err := b.blockImportHandler.HandleBlockProduced(block, ts); err != nil {
+ logger.Warn("failed to import built block", "error", err)
return err
}
diff --git a/lib/babe/babe_test.go b/lib/babe/babe_test.go
index 600a6a9f90..5a9733f483 100644
--- a/lib/babe/babe_test.go
+++ b/lib/babe/babe_test.go
@@ -34,12 +34,14 @@ import (
"github.com/ChainSafe/gossamer/lib/trie"
log "github.com/ChainSafe/log15"
"github.com/stretchr/testify/require"
+
+ "github.com/ChainSafe/gossamer/lib/babe/mocks"
+ mock "github.com/stretchr/testify/mock"
)
var (
defaultTestLogLvl = log.LvlInfo
emptyHash = trie.EmptyHash
- testTimeout = time.Second * 5
testEpochIndex = uint64(0)
maxThreshold = common.MaxUint128
@@ -89,6 +91,9 @@ func createTestService(t *testing.T, cfg *ServiceConfig) *Service {
}
}
+ cfg.BlockImportHandler = new(mocks.BlockImportHandler)
+ cfg.BlockImportHandler.(*mocks.BlockImportHandler).On("HandleBlockProduced", mock.AnythingOfType("*types.Block"), mock.AnythingOfType("*storage.TrieState")).Return(nil)
+
if cfg.Keypair == nil {
cfg.Keypair, err = sr25519.GenerateKeypair()
require.NoError(t, err)
@@ -166,19 +171,16 @@ func TestMain(m *testing.M) {
os.Exit(code)
}
-func TestRunEpochLengthConfig(t *testing.T) {
+func TestService_RunEpochLengthConfig(t *testing.T) {
cfg := &ServiceConfig{
EpochLength: 5,
}
babeService := createTestService(t, cfg)
-
- if babeService.epochLength != 5 {
- t.Fatal("epoch length not set")
- }
+ require.Equal(t, uint64(5), babeService.epochLength)
}
-func TestSlotDuration(t *testing.T) {
+func TestService_SlotDuration(t *testing.T) {
duration, err := time.ParseDuration("1000ms")
require.NoError(t, err)
@@ -190,7 +192,7 @@ func TestSlotDuration(t *testing.T) {
require.Equal(t, dur.Milliseconds(), int64(1000))
}
-func TestBabeAnnounceMessage(t *testing.T) {
+func TestService_ProducesBlocks(t *testing.T) {
babeService := createTestService(t, nil)
babeService.epochData.authorityIndex = 0
@@ -201,21 +203,18 @@ func TestBabeAnnounceMessage(t *testing.T) {
}
babeService.epochData.threshold = maxThreshold
- blockNumber := big.NewInt(int64(1))
err := babeService.Start()
require.NoError(t, err)
+ defer func() {
+ _ = babeService.Stop()
+ }()
- newBlocks := babeService.GetBlockChannel()
- select {
- case block := <-newBlocks:
- require.Equal(t, blockNumber, block.Header.Number)
- case <-time.After(testTimeout):
- t.Fatal("did not receive block")
- }
+ time.Sleep(babeService.slotDuration * 2)
+ babeService.blockImportHandler.(*mocks.BlockImportHandler).AssertCalled(t, "HandleBlockProduced", mock.AnythingOfType("*types.Block"), mock.AnythingOfType("*storage.TrieState"))
}
-func TestGetAuthorityIndex(t *testing.T) {
+func TestService_GetAuthorityIndex(t *testing.T) {
kpA, err := sr25519.GenerateKeypair()
require.NoError(t, err)
diff --git a/lib/babe/errors.go b/lib/babe/errors.go
index a676b21a5e..16f513d839 100644
--- a/lib/babe/errors.go
+++ b/lib/babe/errors.go
@@ -21,46 +21,46 @@ import (
"github.com/ChainSafe/gossamer/lib/scale"
)
-// ErrBadSlotClaim is returned when a slot claim is invalid
-var ErrBadSlotClaim = errors.New("could not verify slot claim VRF proof")
+var (
+ // ErrBadSlotClaim is returned when a slot claim is invalid
+ ErrBadSlotClaim = errors.New("could not verify slot claim VRF proof")
-// ErrBadSecondarySlotClaim is returned when a slot claim is invalid
-var ErrBadSecondarySlotClaim = errors.New("invalid secondary slot claim")
+ // ErrBadSecondarySlotClaim is returned when a slot claim is invalid
+ ErrBadSecondarySlotClaim = errors.New("invalid secondary slot claim")
-// ErrBadSignature is returned when a seal is invalid
-var ErrBadSignature = errors.New("could not verify signature")
+ // ErrBadSignature is returned when a seal is invalid
+ ErrBadSignature = errors.New("could not verify signature")
-// ErrProducerEquivocated is returned when a block producer has produced conflicting blocks
-var ErrProducerEquivocated = errors.New("block producer equivocated")
+ // ErrProducerEquivocated is returned when a block producer has produced conflicting blocks
+ ErrProducerEquivocated = errors.New("block producer equivocated")
-// ErrNilBlockState is returned when the BlockState is nil
-var ErrNilBlockState = errors.New("cannot have nil BlockState")
+ // ErrNotAuthorized is returned when the node is not authorized to produce a block
+ ErrNotAuthorized = errors.New("not authorized to produce block")
-// ErrNilEpochState is returned when the EpochState is nil
-var ErrNilEpochState = errors.New("cannot have nil EpochState")
+ // ErrNoBABEHeader is returned when there is no BABE header found for a block, specifically when calculating randomness
+ ErrNoBABEHeader = errors.New("no BABE header found for block")
-// ErrNotAuthorized is returned when the node is not authorized to produce a block
-var ErrNotAuthorized = errors.New("not authorized to produce block")
+ // ErrVRFOutputOverThreshold is returned when the vrf output for a block is invalid
+ ErrVRFOutputOverThreshold = errors.New("vrf output over threshold")
-// ErrNoBABEHeader is returned when there is no BABE header found for a block, specifically when calculating randomness
-var ErrNoBABEHeader = errors.New("no BABE header found for block")
+ // ErrInvalidBlockProducerIndex is returned when the producer of a block isn't in the authority set
+ ErrInvalidBlockProducerIndex = errors.New("block producer is not in authority set")
-// ErrVRFOutputOverThreshold is returned when the vrf output for a block is invalid
-var ErrVRFOutputOverThreshold = errors.New("vrf output over threshold")
+ // ErrAuthorityAlreadyDisabled is returned when attempting to disabled an already-disabled authority
+ ErrAuthorityAlreadyDisabled = errors.New("authority has already been disabled")
-// ErrInvalidBlockProducerIndex is returned when the producer of a block isn't in the authority set
-var ErrInvalidBlockProducerIndex = errors.New("block producer is not in authority set")
+ // ErrAuthorityDisabled is returned when attempting to verify a block produced by a disabled authority
+ ErrAuthorityDisabled = errors.New("authority has been disabled for the remaining slots in the epoch")
-// ErrAuthorityAlreadyDisabled is returned when attempting to disabled an already-disabled authority
-var ErrAuthorityAlreadyDisabled = errors.New("authority has already been disabled")
+ // ErrNotAuthority is returned when trying to perform authority functions when not an authority
+ ErrNotAuthority = errors.New("node is not an authority")
-// ErrAuthorityDisabled is returned when attempting to verify a block produced by a disabled authority
-var ErrAuthorityDisabled = errors.New("authority has been disabled for the remaining slots in the epoch")
-
-// ErrNotAuthority is returned when trying to perform authority functions when not an authority
-var ErrNotAuthority = errors.New("node is not an authority")
-
-var errInvalidResult = errors.New("invalid error value")
+ errNilBlockImportHandler = errors.New("cannot have nil BlockImportHandler")
+ errNilBlockState = errors.New("cannot have nil BlockState")
+ errNilEpochState = errors.New("cannot have nil EpochState")
+ errNilRuntime = errors.New("runtime is nil")
+ errInvalidResult = errors.New("invalid error value")
+)
// A DispatchOutcomeError is outcome of dispatching the extrinsic
type DispatchOutcomeError struct {
diff --git a/lib/babe/mocks/BlockImportHandler.go b/lib/babe/mocks/BlockImportHandler.go
new file mode 100644
index 0000000000..4e2bc5f5f2
--- /dev/null
+++ b/lib/babe/mocks/BlockImportHandler.go
@@ -0,0 +1,28 @@
+// Code generated by mockery v2.8.0. DO NOT EDIT.
+
+package mocks
+
+import (
+ types "github.com/ChainSafe/gossamer/dot/types"
+ storage "github.com/ChainSafe/gossamer/lib/runtime/storage"
+ mock "github.com/stretchr/testify/mock"
+)
+
+// BlockImportHandler is an autogenerated mock type for the BlockImportHandler type
+type BlockImportHandler struct {
+ mock.Mock
+}
+
+// HandleBlockProduced provides a mock function with given fields: block, state
+func (_m *BlockImportHandler) HandleBlockProduced(block *types.Block, state *storage.TrieState) error {
+ ret := _m.Called(block, state)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(*types.Block, *storage.TrieState) error); ok {
+ r0 = rf(block, state)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
diff --git a/lib/babe/state.go b/lib/babe/state.go
index 373ec91ccc..c86705f978 100644
--- a/lib/babe/state.go
+++ b/lib/babe/state.go
@@ -73,3 +73,8 @@ type EpochState interface {
GetLatestEpochData() (*types.EpochData, error)
SkipVerify(*types.Header) (bool, error)
}
+
+// BlockImportHandler is the interface for the handler of new blocks
+type BlockImportHandler interface {
+ HandleBlockProduced(block *types.Block, state *rtstorage.TrieState) error
+}
diff --git a/lib/babe/verify.go b/lib/babe/verify.go
index 533459b77e..080079626a 100644
--- a/lib/babe/verify.go
+++ b/lib/babe/verify.go
@@ -58,11 +58,11 @@ type VerificationManager struct {
// NewVerificationManager returns a new NewVerificationManager
func NewVerificationManager(blockState BlockState, epochState EpochState) (*VerificationManager, error) {
if blockState == nil {
- return nil, ErrNilBlockState
+ return nil, errNilBlockState
}
if epochState == nil {
- return nil, ErrNilEpochState
+ return nil, errNilEpochState
}
return &VerificationManager{
@@ -304,7 +304,7 @@ type verifier struct {
// newVerifier returns a Verifier for the epoch described by the given descriptor
func newVerifier(blockState BlockState, epoch uint64, info *verifierInfo) (*verifier, error) {
if blockState == nil {
- return nil, ErrNilBlockState
+ return nil, errNilBlockState
}
return &verifier{
diff --git a/lib/grandpa/message_handler_test.go b/lib/grandpa/message_handler_test.go
index 04e7f30dca..077c5fc1fb 100644
--- a/lib/grandpa/message_handler_test.go
+++ b/lib/grandpa/message_handler_test.go
@@ -239,6 +239,9 @@ func TestMessageHandler_CommitMessage_NoCatchUpRequest_ValidSig(t *testing.T) {
fm := gs.newCommitMessage(gs.head, round)
fm.Vote = NewVote(testHash, uint32(round))
+ err := st.Block.SetHeader(testHeader)
+ require.NoError(t, err)
+
h := NewMessageHandler(gs, st.Block)
out, err := h.handleMessage("", fm)
require.NoError(t, err)
@@ -325,7 +328,10 @@ func TestMessageHandler_CatchUpRequest_WithResponse(t *testing.T) {
number: 1,
}
- err := gs.blockState.SetFinalizedHash(testHeader.Hash(), round, setID)
+ err := st.Block.SetHeader(testHeader)
+ require.NoError(t, err)
+
+ err = gs.blockState.SetFinalizedHash(testHeader.Hash(), round, setID)
require.NoError(t, err)
err = gs.blockState.(*state.BlockState).SetHeader(testHeader)
require.NoError(t, err)
diff --git a/lib/grandpa/message_test.go b/lib/grandpa/message_test.go
index f9b61282ec..bc985cd45f 100644
--- a/lib/grandpa/message_test.go
+++ b/lib/grandpa/message_test.go
@@ -97,7 +97,7 @@ func TestCommitMessageToConsensusMessage(t *testing.T) {
}
func TestNewCatchUpResponse(t *testing.T) {
- gs, _ := newTestService(t)
+ gs, st := newTestService(t)
round := uint64(1)
setID := uint64(1)
@@ -111,7 +111,10 @@ func TestNewCatchUpResponse(t *testing.T) {
number: 1,
}
- err := gs.blockState.SetFinalizedHash(testHeader.Hash(), round, setID)
+ err := st.Block.SetHeader(testHeader)
+ require.NoError(t, err)
+
+ err = gs.blockState.SetFinalizedHash(testHeader.Hash(), round, setID)
require.NoError(t, err)
err = gs.blockState.(*state.BlockState).SetHeader(testHeader)
require.NoError(t, err)