diff --git a/devnet/cmd/scale-down-ecs-service/mocks/ecsapi.go b/devnet/cmd/scale-down-ecs-service/mocks/ecsapi.go index b77faa9dd9..c3dd446d7a 100644 --- a/devnet/cmd/scale-down-ecs-service/mocks/ecsapi.go +++ b/devnet/cmd/scale-down-ecs-service/mocks/ecsapi.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package mocks diff --git a/dot/digest/digest.go b/dot/digest/digest.go index ad17d166d9..631be163f8 100644 --- a/dot/digest/digest.go +++ b/dot/digest/digest.go @@ -8,7 +8,6 @@ import ( "errors" "fmt" - "github.com/ChainSafe/gossamer/dot/state" "github.com/ChainSafe/gossamer/dot/types" "github.com/ChainSafe/gossamer/internal/log" "github.com/ChainSafe/gossamer/lib/services" @@ -17,8 +16,6 @@ import ( var ( _ services.Service = &Handler{} - - ErrDefineNextEpoch = errors.New("cannot define next epoch data and config") ) // Handler is used to handle consensus messages and relevant authority updates to BABE and GRANDPA @@ -242,9 +239,14 @@ func (h *Handler) handleBlockFinalisation(ctx context.Context) { continue } - err := h.persistBABEDigestsForNextEpoch(&info.Header) + err := h.epochState.FinalizeBABENextEpochData(&info.Header) + if err != nil { + h.logger.Errorf("failed to persist babe next epoch data: %s", err) + } + + err = h.epochState.FinalizeBABENextConfigData(&info.Header) if err != nil { - h.logger.Errorf("failed to store babe next epoch digest: %s", err) + h.logger.Errorf("failed to persist babe next epoch config: %s", err) } err = h.handleGrandpaChangesOnFinalization(info.Header.Number) @@ -257,35 +259,6 @@ func (h *Handler) handleBlockFinalisation(ctx context.Context) { } } -// persistBABEDigestsForNextEpoch is called only when a block is finalised -// and defines the correct next epoch data and next config data. -func (h *Handler) persistBABEDigestsForNextEpoch(finalizedHeader *types.Header) error { - currEpoch, err := h.epochState.GetEpochForBlock(finalizedHeader) - if err != nil { - return fmt.Errorf("cannot get epoch for block %d (%s): %w", - finalizedHeader.Number, finalizedHeader.Hash(), err) - } - - nextEpoch := currEpoch + 1 - err = h.epochState.FinalizeBABENextEpochData(nextEpoch) - if err != nil && !errors.Is(err, state.ErrEpochNotInMemory) { - return fmt.Errorf("cannot finalize babe next epoch data for block number %d (%s): %w", - finalizedHeader.Number, finalizedHeader.Hash(), err) - } - - err = h.epochState.FinalizeBABENextConfigData(nextEpoch) - if err == nil { - return nil - } else if errors.Is(err, state.ErrEpochNotInMemory) { - return fmt.Errorf("%w: %s", ErrDefineNextEpoch, err) - } - - // the epoch state does not contains any information about the next epoch - return fmt.Errorf("cannot finalize babe next config data for block number %d (%s): %w", - finalizedHeader.Number, finalizedHeader.Hash(), err) - -} - func (h *Handler) handleGrandpaChangesOnImport(num uint) error { resume := h.grandpaResume if resume != nil && num >= resume.atBlock { diff --git a/dot/digest/interface.go b/dot/digest/interface.go index 22c2fda1e5..268ae36c7b 100644 --- a/dot/digest/interface.go +++ b/dot/digest/interface.go @@ -18,6 +18,8 @@ type BlockState interface { FreeFinalisedNotifierChannel(ch chan *types.FinalisationInfo) } +//go:generate mockgen -destination=mock_epoch_state_test.go -package $GOPACKAGE . EpochState + // EpochState is the interface for state.EpochState type EpochState interface { GetEpochForBlock(header *types.Header) (uint64, error) @@ -26,8 +28,8 @@ type EpochState interface { StoreBABENextEpochData(epoch uint64, hash common.Hash, nextEpochData types.NextEpochData) StoreBABENextConfigData(epoch uint64, hash common.Hash, nextEpochData types.NextConfigData) - FinalizeBABENextEpochData(epoch uint64) error - FinalizeBABENextConfigData(epoch uint64) error + FinalizeBABENextEpochData(finalizedHeader *types.Header) error + FinalizeBABENextConfigData(finalizedHeader *types.Header) error } // GrandpaState is the interface for the state.GrandpaState diff --git a/dot/digest/mock_epoch_state_test.go b/dot/digest/mock_epoch_state_test.go new file mode 100644 index 0000000000..943e4cad7d --- /dev/null +++ b/dot/digest/mock_epoch_state_test.go @@ -0,0 +1,131 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/ChainSafe/gossamer/dot/digest (interfaces: EpochState) + +// Package digest is a generated GoMock package. +package digest + +import ( + reflect "reflect" + + types "github.com/ChainSafe/gossamer/dot/types" + common "github.com/ChainSafe/gossamer/lib/common" + gomock "github.com/golang/mock/gomock" +) + +// MockEpochState is a mock of EpochState interface. +type MockEpochState struct { + ctrl *gomock.Controller + recorder *MockEpochStateMockRecorder +} + +// MockEpochStateMockRecorder is the mock recorder for MockEpochState. +type MockEpochStateMockRecorder struct { + mock *MockEpochState +} + +// NewMockEpochState creates a new mock instance. +func NewMockEpochState(ctrl *gomock.Controller) *MockEpochState { + mock := &MockEpochState{ctrl: ctrl} + mock.recorder = &MockEpochStateMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockEpochState) EXPECT() *MockEpochStateMockRecorder { + return m.recorder +} + +// FinalizeBABENextConfigData mocks base method. +func (m *MockEpochState) FinalizeBABENextConfigData(arg0 *types.Header) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FinalizeBABENextConfigData", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// FinalizeBABENextConfigData indicates an expected call of FinalizeBABENextConfigData. +func (mr *MockEpochStateMockRecorder) FinalizeBABENextConfigData(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FinalizeBABENextConfigData", reflect.TypeOf((*MockEpochState)(nil).FinalizeBABENextConfigData), arg0) +} + +// FinalizeBABENextEpochData mocks base method. +func (m *MockEpochState) FinalizeBABENextEpochData(arg0 *types.Header) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "FinalizeBABENextEpochData", arg0) + ret0, _ := ret[0].(error) + return ret0 +} + +// FinalizeBABENextEpochData indicates an expected call of FinalizeBABENextEpochData. +func (mr *MockEpochStateMockRecorder) FinalizeBABENextEpochData(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FinalizeBABENextEpochData", reflect.TypeOf((*MockEpochState)(nil).FinalizeBABENextEpochData), arg0) +} + +// GetEpochForBlock mocks base method. +func (m *MockEpochState) GetEpochForBlock(arg0 *types.Header) (uint64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetEpochForBlock", arg0) + ret0, _ := ret[0].(uint64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetEpochForBlock indicates an expected call of GetEpochForBlock. +func (mr *MockEpochStateMockRecorder) GetEpochForBlock(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEpochForBlock", reflect.TypeOf((*MockEpochState)(nil).GetEpochForBlock), arg0) +} + +// SetConfigData mocks base method. +func (m *MockEpochState) SetConfigData(arg0 uint64, arg1 *types.ConfigData) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetConfigData", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetConfigData indicates an expected call of SetConfigData. +func (mr *MockEpochStateMockRecorder) SetConfigData(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetConfigData", reflect.TypeOf((*MockEpochState)(nil).SetConfigData), arg0, arg1) +} + +// SetEpochData mocks base method. +func (m *MockEpochState) SetEpochData(arg0 uint64, arg1 *types.EpochData) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetEpochData", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetEpochData indicates an expected call of SetEpochData. +func (mr *MockEpochStateMockRecorder) SetEpochData(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetEpochData", reflect.TypeOf((*MockEpochState)(nil).SetEpochData), arg0, arg1) +} + +// StoreBABENextConfigData mocks base method. +func (m *MockEpochState) StoreBABENextConfigData(arg0 uint64, arg1 common.Hash, arg2 types.NextConfigData) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "StoreBABENextConfigData", arg0, arg1, arg2) +} + +// StoreBABENextConfigData indicates an expected call of StoreBABENextConfigData. +func (mr *MockEpochStateMockRecorder) StoreBABENextConfigData(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StoreBABENextConfigData", reflect.TypeOf((*MockEpochState)(nil).StoreBABENextConfigData), arg0, arg1, arg2) +} + +// StoreBABENextEpochData mocks base method. +func (m *MockEpochState) StoreBABENextEpochData(arg0 uint64, arg1 common.Hash, arg2 types.NextEpochData) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "StoreBABENextEpochData", arg0, arg1, arg2) +} + +// StoreBABENextEpochData indicates an expected call of StoreBABENextEpochData. +func (mr *MockEpochStateMockRecorder) StoreBABENextEpochData(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StoreBABENextEpochData", reflect.TypeOf((*MockEpochState)(nil).StoreBABENextEpochData), arg0, arg1, arg2) +} diff --git a/dot/rpc/modules/mocks/block_api.go b/dot/rpc/modules/mocks/block_api.go index 8fae0eb002..7fa7736ddb 100644 --- a/dot/rpc/modules/mocks/block_api.go +++ b/dot/rpc/modules/mocks/block_api.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package mocks diff --git a/dot/rpc/modules/mocks/block_finality_api.go b/dot/rpc/modules/mocks/block_finality_api.go index cd163ce5c7..21117ca358 100644 --- a/dot/rpc/modules/mocks/block_finality_api.go +++ b/dot/rpc/modules/mocks/block_finality_api.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package mocks diff --git a/dot/rpc/modules/mocks/block_producer_api.go b/dot/rpc/modules/mocks/block_producer_api.go index a18e2ace04..fd40639138 100644 --- a/dot/rpc/modules/mocks/block_producer_api.go +++ b/dot/rpc/modules/mocks/block_producer_api.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package mocks diff --git a/dot/rpc/modules/mocks/core_api.go b/dot/rpc/modules/mocks/core_api.go index baacb5b5b0..59105fbd01 100644 --- a/dot/rpc/modules/mocks/core_api.go +++ b/dot/rpc/modules/mocks/core_api.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package mocks diff --git a/dot/rpc/modules/mocks/network_api.go b/dot/rpc/modules/mocks/network_api.go index 8ab4e28bec..76871f8469 100644 --- a/dot/rpc/modules/mocks/network_api.go +++ b/dot/rpc/modules/mocks/network_api.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package mocks diff --git a/dot/rpc/modules/mocks/rpcapi.go b/dot/rpc/modules/mocks/rpcapi.go index e5be5e8790..a65beb315e 100644 --- a/dot/rpc/modules/mocks/rpcapi.go +++ b/dot/rpc/modules/mocks/rpcapi.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package mocks diff --git a/dot/rpc/modules/mocks/runtime_storage_api.go b/dot/rpc/modules/mocks/runtime_storage_api.go index 0380c1cf82..840c96d059 100644 --- a/dot/rpc/modules/mocks/runtime_storage_api.go +++ b/dot/rpc/modules/mocks/runtime_storage_api.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package mocks diff --git a/dot/rpc/modules/mocks/storage_api.go b/dot/rpc/modules/mocks/storage_api.go index 2b4557ce82..8cb3fbe5bf 100644 --- a/dot/rpc/modules/mocks/storage_api.go +++ b/dot/rpc/modules/mocks/storage_api.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package mocks diff --git a/dot/rpc/modules/mocks/sync_state_api.go b/dot/rpc/modules/mocks/sync_state_api.go index cfbc014ea4..0e9e8c078b 100644 --- a/dot/rpc/modules/mocks/sync_state_api.go +++ b/dot/rpc/modules/mocks/sync_state_api.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package mocks diff --git a/dot/rpc/modules/mocks/system_api.go b/dot/rpc/modules/mocks/system_api.go index f8f39b0c6a..21889736fb 100644 --- a/dot/rpc/modules/mocks/system_api.go +++ b/dot/rpc/modules/mocks/system_api.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package mocks diff --git a/dot/rpc/modules/mocks/transaction_state_api.go b/dot/rpc/modules/mocks/transaction_state_api.go index d783820fa4..82fc6e7553 100644 --- a/dot/rpc/modules/mocks/transaction_state_api.go +++ b/dot/rpc/modules/mocks/transaction_state_api.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package mocks diff --git a/dot/state/epoch.go b/dot/state/epoch.go index d77ae8fc32..cdc3fba584 100644 --- a/dot/state/epoch.go +++ b/dot/state/epoch.go @@ -244,7 +244,7 @@ func (s *EpochState) SetEpochData(epoch uint64, info *types.EpochData) error { // otherwise will try to get the data from the in-memory map using the header // if the header params is nil then it will search only in database func (s *EpochState) GetEpochData(epoch uint64, header *types.Header) (*types.EpochData, error) { - epochData, err := s.getEpochDataInDatabase(epoch) + epochData, err := s.getEpochDataFromDatabase(epoch) if err == nil && epochData != nil { return epochData, nil } @@ -256,7 +256,7 @@ func (s *EpochState) GetEpochData(epoch uint64, header *types.Header) (*types.Ep return epochData, nil } - epochData, err = s.getEpochDataInMemory(epoch, header) + epochData, err = s.getEpochDataFromMemory(epoch, header) if err != nil { return nil, fmt.Errorf("failed to get epoch data from memory: %w", err) } @@ -264,8 +264,8 @@ func (s *EpochState) GetEpochData(epoch uint64, header *types.Header) (*types.Ep return epochData, nil } -// getEpochDataInDatabase returns the epoch data for a given epoch persisted in database -func (s *EpochState) getEpochDataInDatabase(epoch uint64) (*types.EpochData, error) { +// getEpochDataFromDatabase returns the epoch data for a given epoch persisted in database +func (s *EpochState) getEpochDataFromDatabase(epoch uint64) (*types.EpochData, error) { enc, err := s.db.Get(epochDataKey(epoch)) if err != nil { return nil, err @@ -280,8 +280,8 @@ func (s *EpochState) getEpochDataInDatabase(epoch uint64) (*types.EpochData, err return raw.ToEpochData() } -// getEpochDataInMemory retrieves the right epoch data that belongs to the header parameter -func (s *EpochState) getEpochDataInMemory(epoch uint64, header *types.Header) (*types.EpochData, error) { +// getEpochDataFromMemory retrieves the right epoch data that belongs to the header parameter +func (s *EpochState) getEpochDataFromMemory(epoch uint64, header *types.Header) (*types.EpochData, error) { s.nextEpochDataLock.RLock() defer s.nextEpochDataLock.RUnlock() @@ -359,7 +359,7 @@ func (s *EpochState) setLatestConfigData(epoch uint64) error { // otherwise tries to get the data from the in-memory map using the header. // If the header params is nil then it will search only in the database func (s *EpochState) GetConfigData(epoch uint64, header *types.Header) (*types.ConfigData, error) { - configData, err := s.getConfigDataInDatabase(epoch) + configData, err := s.getConfigDataFromDatabase(epoch) if err == nil && configData != nil { return configData, nil } @@ -371,7 +371,7 @@ func (s *EpochState) GetConfigData(epoch uint64, header *types.Header) (*types.C return configData, nil } - configData, err = s.getConfigDataInMemory(epoch, header) + configData, err = s.getConfigDataFromMemory(epoch, header) if err != nil { return nil, fmt.Errorf("failed to get config data from memory: %w", err) } @@ -379,8 +379,8 @@ func (s *EpochState) GetConfigData(epoch uint64, header *types.Header) (*types.C return configData, nil } -// getConfigDataInDatabase returns the BABE config data for a given epoch persisted in database -func (s *EpochState) getConfigDataInDatabase(epoch uint64) (*types.ConfigData, error) { +// getConfigDataFromDatabase returns the BABE config data for a given epoch persisted in database +func (s *EpochState) getConfigDataFromDatabase(epoch uint64) (*types.ConfigData, error) { enc, err := s.db.Get(configDataKey(epoch)) if err != nil { return nil, err @@ -395,8 +395,8 @@ func (s *EpochState) getConfigDataInDatabase(epoch uint64) (*types.ConfigData, e return info, nil } -// getConfigDataInMemory retrieves the BABE config data for a given epoch that belongs to the header parameter -func (s *EpochState) getConfigDataInMemory(epoch uint64, header *types.Header) (*types.ConfigData, error) { +// getConfigDataFromMemory retrieves the BABE config data for a given epoch that belongs to the header parameter +func (s *EpochState) getConfigDataFromMemory(epoch uint64, header *types.Header) (*types.ConfigData, error) { s.nextConfigDataLock.RLock() defer s.nextConfigDataLock.RUnlock() @@ -540,11 +540,32 @@ func (s *EpochState) StoreBABENextConfigData(epoch uint64, hash common.Hash, nex // getting the set of hashes from the received epoch and for each hash // check if the header is in the database then it's been finalized and // thus we can also set the corresponding EpochData in the database -func (s *EpochState) FinalizeBABENextEpochData(epoch uint64) error { +func (s *EpochState) FinalizeBABENextEpochData(finalizedHeader *types.Header) error { s.nextEpochDataLock.Lock() defer s.nextEpochDataLock.Unlock() - finalizedNextEpochData, err := lookupForNextEpochPersistedHash(s.nextEpochData, s, epoch) + finalizedBlockEpoch, err := s.GetEpochForBlock(finalizedHeader) + if err != nil { + return fmt.Errorf("cannot get epoch for block %d (%s): %w", + finalizedHeader.Number, finalizedHeader.Hash(), err) + } + + nextEpoch := finalizedBlockEpoch + 1 + + epochInDatabase, err := s.getEpochDataFromDatabase(nextEpoch) + + // if an error occurs and the error is chaindb.ErrKeyNotFound we ignore + // since this error is what we will handle in the next lines + if err != nil && !errors.Is(err, chaindb.ErrKeyNotFound) { + return fmt.Errorf("cannot check if next epoch data is already defined for epoch %d: %w", nextEpoch, err) + } + + // epoch data already defined we don't need to lookup in the map + if epochInDatabase != nil { + return nil + } + + finalizedNextEpochData, err := findFinalizedHeaderForEpoch(s.nextEpochData, s, nextEpoch) if err != nil { return fmt.Errorf("cannot find next epoch data: %w", err) } @@ -554,14 +575,14 @@ func (s *EpochState) FinalizeBABENextEpochData(epoch uint64) error { return fmt.Errorf("cannot transform epoch data: %w", err) } - err = s.SetEpochData(epoch, ed) + err = s.SetEpochData(nextEpoch, ed) if err != nil { return fmt.Errorf("cannot set epoch data: %w", err) } // remove previous epochs from the memory for e := range s.nextEpochData { - if e <= epoch { + if e <= nextEpoch { delete(s.nextEpochData, e) } } @@ -573,24 +594,49 @@ func (s *EpochState) FinalizeBABENextEpochData(epoch uint64) error { // getting the set of hashes from the received epoch and for each hash // check if the header is in the database then it's been finalized and // thus we can also set the corresponding NextConfigData in the database -func (s *EpochState) FinalizeBABENextConfigData(epoch uint64) error { +func (s *EpochState) FinalizeBABENextConfigData(finalizedHeader *types.Header) error { s.nextConfigDataLock.Lock() defer s.nextConfigDataLock.Unlock() - finalizedNextConfigData, err := lookupForNextEpochPersistedHash(s.nextConfigData, s, epoch) + finalizedBlockEpoch, err := s.GetEpochForBlock(finalizedHeader) if err != nil { + return fmt.Errorf("cannot get epoch for block %d (%s): %w", + finalizedHeader.Number, finalizedHeader.Hash(), err) + } + + nextEpoch := finalizedBlockEpoch + 1 + + configInDatabase, err := s.getConfigDataFromDatabase(nextEpoch) + + // if an error occurs and the error is chaindb.ErrKeyNotFound we ignore + // since this error is what we will handle in the next lines + if err != nil && !errors.Is(err, chaindb.ErrKeyNotFound) { + return fmt.Errorf("cannot check if next epoch config is already defined for epoch %d: %w", nextEpoch, err) + } + + // config data already defined we don't need to lookup in the map + if configInDatabase != nil { + return nil + } + + // not every epoch will have `ConfigData` + finalizedNextConfigData, err := findFinalizedHeaderForEpoch(s.nextConfigData, s, nextEpoch) + if errors.Is(err, ErrEpochNotInMemory) { + logger.Debugf("config data for epoch %d not found in memory", nextEpoch) + return nil + } else if err != nil { return fmt.Errorf("cannot find next config data: %w", err) } cd := finalizedNextConfigData.ToConfigData() - err = s.SetConfigData(epoch, cd) + err = s.SetConfigData(nextEpoch, cd) if err != nil { return fmt.Errorf("cannot set config data: %w", err) } // remove previous epochs from the memory for e := range s.nextConfigData { - if e <= epoch { + if e <= nextEpoch { delete(s.nextConfigData, e) } } @@ -598,10 +644,10 @@ func (s *EpochState) FinalizeBABENextConfigData(epoch uint64) error { return nil } -// lookupForNextEpochPersistedHash given a specific epoch (the key) will go through the hashes looking +// findFinalizedHeaderForEpoch given a specific epoch (the key) will go through the hashes looking // for a database persisted hash (belonging to the finalized chain) -// which contains the right configuration to be persisted and safely used -func lookupForNextEpochPersistedHash[T types.NextConfigData | types.NextEpochData]( +// which contains the right configuration or data to be persisted and safely used +func findFinalizedHeaderForEpoch[T types.NextConfigData | types.NextEpochData]( nextEpochMap map[uint64]map[common.Hash]T, es *EpochState, epoch uint64) (next *T, err error) { hashes, has := nextEpochMap[epoch] if !has { diff --git a/dot/state/epoch_test.go b/dot/state/epoch_test.go index f3b4247890..234e22ca55 100644 --- a/dot/state/epoch_test.go +++ b/dot/state/epoch_test.go @@ -226,10 +226,10 @@ func TestEpochState_GetEpochFromTime(t *testing.T) { require.Equal(t, uint64(99), epoch) } -type inMemoryNextEpochData struct { - epoch uint64 - hashes []common.Hash - nextEpochDatas []types.NextEpochData +type inMemoryBABEData[T any] struct { + epoch uint64 + hashes []common.Hash + nextData []T } func TestStoreAndFinalizeBabeNextEpochData(t *testing.T) { @@ -252,9 +252,32 @@ func TestStoreAndFinalizeBabeNextEpochData(t *testing.T) { } } + babePrimaryPreDigest := types.BabePrimaryPreDigest{ + SlotNumber: 301, // block on epoch 1 with digest for epoch 2 + VRFOutput: [32]byte{}, + VRFProof: [64]byte{}, + } + + preRuntimeDigest, err := babePrimaryPreDigest.ToPreRuntimeDigest() + require.NoError(t, err) + + digest := scale.NewVaryingDataTypeSlice(scale.MustNewVaryingDataType( + types.PreRuntimeDigest{})) + + require.NoError(t, digest.Add(*preRuntimeDigest)) + + // a random finalized header for testing purposes + finalizedHeader := &types.Header{ + ParentHash: common.Hash{}, + Number: 1, + Digest: digest, + } + + finalizedHeaderHash := finalizedHeader.Hash() + tests := map[string]struct { - finalizeHash common.Hash - inMemoryEpoch []inMemoryNextEpochData + finalizedHeader *types.Header + inMemoryEpoch []inMemoryBABEData[types.NextEpochData] finalizeEpoch uint64 expectErr error shouldRemainInMemory int @@ -262,8 +285,8 @@ func TestStoreAndFinalizeBabeNextEpochData(t *testing.T) { "store_and_finalize_successfully": { shouldRemainInMemory: 1, finalizeEpoch: 2, - finalizeHash: common.MustHexToHash("0x68a27df5a52ff2251df2cc8368f7dcefb305a13bb3d89b65c8fb070f23877f2c"), - inMemoryEpoch: []inMemoryNextEpochData{ + finalizedHeader: finalizedHeader, + inMemoryEpoch: []inMemoryBABEData[types.NextEpochData]{ { epoch: 1, hashes: []common.Hash{ @@ -271,7 +294,7 @@ func TestStoreAndFinalizeBabeNextEpochData(t *testing.T) { common.MustHexToHash("0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3"), common.MustHexToHash("0xc0096358534ec8d21d01d34b836eed476a1c343f8724fa2153dc0725ad797a90"), }, - nextEpochDatas: []types.NextEpochData{ + nextData: []types.NextEpochData{ { Authorities: authorities[:3], Randomness: [32]byte{1}, @@ -291,9 +314,9 @@ func TestStoreAndFinalizeBabeNextEpochData(t *testing.T) { hashes: []common.Hash{ common.MustHexToHash("0x5b940c7fc0a1c5a58e4d80c5091dd003303b8f18e90a989f010c1be6f392bed1"), common.MustHexToHash("0xd380bee22de487a707cbda65dd9d4e2188f736908c42cf390c8919d4f7fc547c"), - common.MustHexToHash("0x68a27df5a52ff2251df2cc8368f7dcefb305a13bb3d89b65c8fb070f23877f2c"), + finalizedHeaderHash, }, - nextEpochDatas: []types.NextEpochData{ + nextData: []types.NextEpochData{ { Authorities: authorities[6:], Randomness: [32]byte{1}, @@ -313,7 +336,7 @@ func TestStoreAndFinalizeBabeNextEpochData(t *testing.T) { hashes: []common.Hash{ common.MustHexToHash("0xab5c9230a7dde8bb90a6728ba4a0165423294dac14336b1443f865b796ff682c"), }, - nextEpochDatas: []types.NextEpochData{ + nextData: []types.NextEpochData{ { Authorities: authorities[6:], Randomness: [32]byte{1}, @@ -324,18 +347,19 @@ func TestStoreAndFinalizeBabeNextEpochData(t *testing.T) { }, "cannot_finalize_hash_not_stored": { shouldRemainInMemory: 1, - finalizeEpoch: 1, - finalizeHash: common.Hash{}, // finalize when the hash does not exists - expectErr: errHashNotPersisted, - inMemoryEpoch: []inMemoryNextEpochData{ + finalizeEpoch: 2, + // this header hash is not in the database + finalizedHeader: finalizedHeader, + expectErr: errHashNotPersisted, + inMemoryEpoch: []inMemoryBABEData[types.NextEpochData]{ { - epoch: 1, + epoch: 2, hashes: []common.Hash{ common.MustHexToHash("0x9da3ce2785da743bfbc13449db7dcb7a69c07ca914276d839abe7bedc6ac8fed"), common.MustHexToHash("0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3"), common.MustHexToHash("0xc0096358534ec8d21d01d34b836eed476a1c343f8724fa2153dc0725ad797a90"), }, - nextEpochDatas: []types.NextEpochData{ + nextData: []types.NextEpochData{ { Authorities: authorities[:3], Randomness: [32]byte{1}, @@ -353,34 +377,11 @@ func TestStoreAndFinalizeBabeNextEpochData(t *testing.T) { }, }, "cannot_finalize_in_memory_epoch_not_found": { - shouldRemainInMemory: 1, + shouldRemainInMemory: 0, finalizeEpoch: 3, // try to finalize a epoch that does not exists - finalizeHash: common.Hash{}, + finalizedHeader: finalizedHeader, expectErr: ErrEpochNotInMemory, - inMemoryEpoch: []inMemoryNextEpochData{ - { - epoch: 1, - hashes: []common.Hash{ - common.MustHexToHash("0x9da3ce2785da743bfbc13449db7dcb7a69c07ca914276d839abe7bedc6ac8fed"), - common.MustHexToHash("0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3"), - common.MustHexToHash("0xc0096358534ec8d21d01d34b836eed476a1c343f8724fa2153dc0725ad797a90"), - }, - nextEpochDatas: []types.NextEpochData{ - { - Authorities: authorities[:3], - Randomness: [32]byte{1}, - }, - { - Authorities: authorities[3:6], - Randomness: [32]byte{2}, - }, - { - Authorities: authorities[6:], - Randomness: [32]byte{3}, - }, - }, - }, - }, + inMemoryEpoch: []inMemoryBABEData[types.NextEpochData]{}, }, } @@ -390,18 +391,18 @@ func TestStoreAndFinalizeBabeNextEpochData(t *testing.T) { for _, e := range tt.inMemoryEpoch { for i, hash := range e.hashes { - epochState.StoreBABENextEpochData(e.epoch, hash, e.nextEpochDatas[i]) + epochState.StoreBABENextEpochData(e.epoch, hash, e.nextData[i]) } } require.Len(t, epochState.nextEpochData, len(tt.inMemoryEpoch)) - expectedNextEpochData := epochState.nextEpochData[tt.finalizeEpoch][tt.finalizeHash] + expectedNextEpochData := epochState.nextEpochData[tt.finalizeEpoch][tt.finalizedHeader.Hash()] - err := epochState.blockState.db.Put(headerKey(tt.finalizeHash), []byte{}) + err := epochState.blockState.db.Put(headerKey(tt.finalizedHeader.Hash()), []byte{}) require.NoError(t, err) - err = epochState.FinalizeBABENextEpochData(tt.finalizeEpoch) + err = epochState.FinalizeBABENextEpochData(tt.finalizedHeader) if tt.expectErr != nil { require.ErrorIs(t, err, tt.expectErr) } else { @@ -422,25 +423,42 @@ func TestStoreAndFinalizeBabeNextEpochData(t *testing.T) { } } -type inMemotyNextConfighData struct { - epoch uint64 - hashes []common.Hash - nextConfigDatas []types.NextConfigData -} - func TestStoreAndFinalizeBabeNextConfigData(t *testing.T) { + babePrimaryPreDigest := types.BabePrimaryPreDigest{ + SlotNumber: 301, // block on epoch 1 with changes to epoch 2 + VRFOutput: [32]byte{}, + VRFProof: [64]byte{}, + } + + preRuntimeDigest, err := babePrimaryPreDigest.ToPreRuntimeDigest() + require.NoError(t, err) + + digest := scale.NewVaryingDataTypeSlice(scale.MustNewVaryingDataType( + types.PreRuntimeDigest{})) + + require.NoError(t, digest.Add(*preRuntimeDigest)) + + // finalized header for testing purposes + finalizedHeader := &types.Header{ + ParentHash: common.Hash{}, + Number: 1, + Digest: digest, + } + + finalizedHeaderHash := finalizedHeader.Hash() + tests := map[string]struct { - finalizeHash common.Hash - inMemoryEpoch []inMemotyNextConfighData - finalizeEpoch uint64 + finalizedHeader *types.Header + inMemoryEpoch []inMemoryBABEData[types.NextConfigData] + finalizedEpoch uint64 expectErr error shouldRemainInMemory int }{ "store_and_finalize_successfully": { shouldRemainInMemory: 1, - finalizeEpoch: 2, - finalizeHash: common.MustHexToHash("0x68a27df5a52ff2251df2cc8368f7dcefb305a13bb3d89b65c8fb070f23877f2c"), - inMemoryEpoch: []inMemotyNextConfighData{ + finalizedEpoch: 2, + finalizedHeader: finalizedHeader, + inMemoryEpoch: []inMemoryBABEData[types.NextConfigData]{ { epoch: 1, hashes: []common.Hash{ @@ -448,7 +466,7 @@ func TestStoreAndFinalizeBabeNextConfigData(t *testing.T) { common.MustHexToHash("0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3"), common.MustHexToHash("0xc0096358534ec8d21d01d34b836eed476a1c343f8724fa2153dc0725ad797a90"), }, - nextConfigDatas: []types.NextConfigData{ + nextData: []types.NextConfigData{ { C1: 1, C2: 2, @@ -471,9 +489,9 @@ func TestStoreAndFinalizeBabeNextConfigData(t *testing.T) { hashes: []common.Hash{ common.MustHexToHash("0x5b940c7fc0a1c5a58e4d80c5091dd003303b8f18e90a989f010c1be6f392bed1"), common.MustHexToHash("0xd380bee22de487a707cbda65dd9d4e2188f736908c42cf390c8919d4f7fc547c"), - common.MustHexToHash("0x68a27df5a52ff2251df2cc8368f7dcefb305a13bb3d89b65c8fb070f23877f2c"), + finalizedHeaderHash, }, - nextConfigDatas: []types.NextConfigData{ + nextData: []types.NextConfigData{ { C1: 1, C2: 2, @@ -496,7 +514,7 @@ func TestStoreAndFinalizeBabeNextConfigData(t *testing.T) { hashes: []common.Hash{ common.MustHexToHash("0xab5c9230a7dde8bb90a6728ba4a0165423294dac14336b1443f865b796ff682c"), }, - nextConfigDatas: []types.NextConfigData{ + nextData: []types.NextConfigData{ { C1: 1, C2: 2, @@ -508,18 +526,18 @@ func TestStoreAndFinalizeBabeNextConfigData(t *testing.T) { }, "cannot_finalize_hash_doesnt_exists": { shouldRemainInMemory: 1, - finalizeEpoch: 1, - finalizeHash: common.Hash{}, // finalize when the hash does not exists + finalizedEpoch: 2, + finalizedHeader: finalizedHeader, // finalize when the hash does not exist expectErr: errHashNotPersisted, - inMemoryEpoch: []inMemotyNextConfighData{ + inMemoryEpoch: []inMemoryBABEData[types.NextConfigData]{ { - epoch: 1, + epoch: 2, hashes: []common.Hash{ common.MustHexToHash("0x9da3ce2785da743bfbc13449db7dcb7a69c07ca914276d839abe7bedc6ac8fed"), common.MustHexToHash("0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3"), common.MustHexToHash("0xc0096358534ec8d21d01d34b836eed476a1c343f8724fa2153dc0725ad797a90"), }, - nextConfigDatas: []types.NextConfigData{ + nextData: []types.NextConfigData{ { C1: 1, C2: 2, @@ -539,38 +557,11 @@ func TestStoreAndFinalizeBabeNextConfigData(t *testing.T) { }, }, }, - "cannot_finalize_in_memory_epoch_not_found": { - shouldRemainInMemory: 1, - finalizeEpoch: 3, // try to finalize a epoch that does not exists - finalizeHash: common.Hash{}, - expectErr: ErrEpochNotInMemory, - inMemoryEpoch: []inMemotyNextConfighData{ - { - epoch: 1, - hashes: []common.Hash{ - common.MustHexToHash("0x9da3ce2785da743bfbc13449db7dcb7a69c07ca914276d839abe7bedc6ac8fed"), - common.MustHexToHash("0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3"), - common.MustHexToHash("0xc0096358534ec8d21d01d34b836eed476a1c343f8724fa2153dc0725ad797a90"), - }, - nextConfigDatas: []types.NextConfigData{ - { - C1: 1, - C2: 2, - SecondarySlots: 0, - }, - { - C1: 2, - C2: 3, - SecondarySlots: 1, - }, - { - C1: 3, - C2: 4, - SecondarySlots: 0, - }, - }, - }, - }, + "in_memory_config_not_found_shouldnt_return_error": { + shouldRemainInMemory: 0, + finalizedEpoch: 1, // try to finalize an epoch that does not exist + finalizedHeader: finalizedHeader, + inMemoryEpoch: []inMemoryBABEData[types.NextConfigData]{}, }, } @@ -580,24 +571,32 @@ func TestStoreAndFinalizeBabeNextConfigData(t *testing.T) { for _, e := range tt.inMemoryEpoch { for i, hash := range e.hashes { - epochState.StoreBABENextConfigData(e.epoch, hash, e.nextConfigDatas[i]) + epochState.StoreBABENextConfigData(e.epoch, hash, e.nextData[i]) } } require.Len(t, epochState.nextConfigData, len(tt.inMemoryEpoch)) - expectedConfigData := epochState.nextConfigData[tt.finalizeEpoch][tt.finalizeHash] + // if there is no data in memory we try to finalize the next config data + // it should return nil since next epoch config data will not be in every epoch's first block + if len(tt.inMemoryEpoch) == 0 { + err = epochState.FinalizeBABENextConfigData(tt.finalizedHeader) + require.NoError(t, err) + return + } + + expectedConfigData := epochState.nextConfigData[tt.finalizedEpoch][tt.finalizedHeader.Hash()] - err := epochState.blockState.db.Put(headerKey(tt.finalizeHash), []byte{}) + err := epochState.blockState.db.Put(headerKey(tt.finalizedHeader.Hash()), []byte{}) require.NoError(t, err) - err = epochState.FinalizeBABENextConfigData(tt.finalizeEpoch) + err = epochState.FinalizeBABENextConfigData(tt.finalizedHeader) if tt.expectErr != nil { require.ErrorIs(t, err, tt.expectErr) } else { require.NoError(t, err) - gotConfigData, err := epochState.GetConfigData(tt.finalizeEpoch, nil) + gotConfigData, err := epochState.GetConfigData(tt.finalizedEpoch, nil) require.NoError(t, err) require.Equal(t, expectedConfigData.ToConfigData(), gotConfigData) } diff --git a/dot/state/mock_observer.go b/dot/state/mock_observer.go index 6a0683586d..dfdb42adcc 100644 --- a/dot/state/mock_observer.go +++ b/dot/state/mock_observer.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package state diff --git a/dot/sync/mocks/babe_verifier.go b/dot/sync/mocks/babe_verifier.go index 0be36b3ad8..3b11bbf29d 100644 --- a/dot/sync/mocks/babe_verifier.go +++ b/dot/sync/mocks/babe_verifier.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package mocks diff --git a/dot/sync/mocks/block_import_handler.go b/dot/sync/mocks/block_import_handler.go index 10488e6f7a..6b4cd46b3f 100644 --- a/dot/sync/mocks/block_import_handler.go +++ b/dot/sync/mocks/block_import_handler.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package mocks diff --git a/dot/sync/mocks/block_state.go b/dot/sync/mocks/block_state.go index 367601d550..2f9d48d9af 100644 --- a/dot/sync/mocks/block_state.go +++ b/dot/sync/mocks/block_state.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package mocks diff --git a/dot/sync/mocks/finality_gadget.go b/dot/sync/mocks/finality_gadget.go index f3c1fc3e5e..6179f5d6fd 100644 --- a/dot/sync/mocks/finality_gadget.go +++ b/dot/sync/mocks/finality_gadget.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package mocks diff --git a/dot/sync/mocks/network.go b/dot/sync/mocks/network.go index d1b945bde6..d9c8accaf5 100644 --- a/dot/sync/mocks/network.go +++ b/dot/sync/mocks/network.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package mocks diff --git a/lib/babe/mocks/block_import_handler.go b/lib/babe/mocks/block_import_handler.go index c803e1f65b..c4c61cf002 100644 --- a/lib/babe/mocks/block_import_handler.go +++ b/lib/babe/mocks/block_import_handler.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package mocks diff --git a/lib/grandpa/mocks/network.go b/lib/grandpa/mocks/network.go index a1930740db..5d7e607e03 100644 --- a/lib/grandpa/mocks/network.go +++ b/lib/grandpa/mocks/network.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package mocks diff --git a/lib/runtime/mock_memory_test.go b/lib/runtime/mock_memory_test.go index 97374c51cc..9fca62f217 100644 --- a/lib/runtime/mock_memory_test.go +++ b/lib/runtime/mock_memory_test.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package runtime diff --git a/lib/runtime/mocks/instance.go b/lib/runtime/mocks/instance.go index 9188ba2349..c8f2353909 100644 --- a/lib/runtime/mocks/instance.go +++ b/lib/runtime/mocks/instance.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package mocks diff --git a/lib/runtime/mocks/transaction_state.go b/lib/runtime/mocks/transaction_state.go index 952ecfad4e..f7dd59036c 100644 --- a/lib/runtime/mocks/transaction_state.go +++ b/lib/runtime/mocks/transaction_state.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package mocks diff --git a/lib/runtime/mocks/version.go b/lib/runtime/mocks/version.go index b389875077..1999794717 100644 --- a/lib/runtime/mocks/version.go +++ b/lib/runtime/mocks/version.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package mocks diff --git a/lib/services/mocks/service.go b/lib/services/mocks/service.go index 98752d2740..d6af8c0324 100644 --- a/lib/services/mocks/service.go +++ b/lib/services/mocks/service.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.10.4. DO NOT EDIT. +// Code generated by mockery v2.10.6. DO NOT EDIT. package mocks