Skip to content
This repository has been archived by the owner on Aug 1, 2024. It is now read-only.
/ cosmos-sdk Public archive
forked from cosmos/cosmos-sdk

Commit

Permalink
feat: Implement BeginBlock EndBlock for Core API modules (cosmos#14819)
Browse files Browse the repository at this point in the history
Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com>
  • Loading branch information
2 people authored and tsenart committed Apr 12, 2023
1 parent 966ffc6 commit e36572d
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 24 deletions.
28 changes: 28 additions & 0 deletions testutil/mock/types_mock_appmodule.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions types/module/mock_appmodule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ type AppModuleWithAllExtensions interface {
type CoreAppModule interface {
appmodule.AppModule
appmodule.HasGenesis
appmodule.HasBeginBlocker
appmodule.HasEndBlocker
}
39 changes: 24 additions & 15 deletions types/module/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ func (m *Manager) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, genesisData

// a chain must initialize with a non-empty validator set
if len(validatorUpdates) == 0 {
panic(fmt.Sprintf("validator set is empty after InitGenesis, please ensure at least one validator is initialized with a delegation greater than or equal to the DefaultPowerReduction (%d)", sdk.DefaultPowerReduction))
return abci.ResponseInitChain{}, fmt.Errorf("validator set is empty after InitGenesis, please ensure at least one validator is initialized with a delegation greater than or equal to the DefaultPowerReduction (%d)", sdk.DefaultPowerReduction)
}

return abci.ResponseInitChain{
Expand Down Expand Up @@ -640,9 +640,13 @@ func (m *Manager) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) (abci.
ctx = ctx.WithEventManager(sdk.NewEventManager())

for _, moduleName := range m.OrderBeginBlockers {
module, ok := m.Modules[moduleName].(BeginBlockAppModule)
if ok {
if module, ok := m.Modules[moduleName].(BeginBlockAppModule); ok {
module.BeginBlock(ctx, req)
} else if module, ok := m.Modules[moduleName].(appmodule.HasBeginBlocker); ok {
err := module.BeginBlock(ctx)
if err != nil {
return abci.ResponseBeginBlock{}, err
}
}
}

Expand All @@ -659,20 +663,25 @@ func (m *Manager) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) (abci.Resp
validatorUpdates := []abci.ValidatorUpdate{}

for _, moduleName := range m.OrderEndBlockers {
module, ok := m.Modules[moduleName].(EndBlockAppModule)
if !ok {
continue
}
moduleValUpdates := module.EndBlock(ctx, req)
if module, ok := m.Modules[moduleName].(EndBlockAppModule); ok {
moduleValUpdates := module.EndBlock(ctx, req)

// use these validator updates if provided, the module manager assumes
// only one module will update the validator set
if len(moduleValUpdates) > 0 {
if len(validatorUpdates) > 0 {
return abci.ResponseEndBlock{}, errors.New("validator EndBlock updates already set by a previous module")
}
// use these validator updates if provided, the module manager assumes
// only one module will update the validator set
if len(moduleValUpdates) > 0 {
if len(validatorUpdates) > 0 {
return abci.ResponseEndBlock{}, errors.New("validator EndBlock updates already set by a previous module")
}

validatorUpdates = moduleValUpdates
validatorUpdates = moduleValUpdates
}
} else if module, ok := m.Modules[moduleName].(appmodule.HasEndBlocker); ok {
err := module.EndBlock(ctx)
if err != nil {
return abci.ResponseEndBlock{}, err
}
} else {
continue
}
}

Expand Down
79 changes: 70 additions & 9 deletions types/module/module_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,8 @@ func TestManager_InitGenesis(t *testing.T) {

// this should panic since the validator set is empty even after init genesis
mockAppModule1.EXPECT().InitGenesis(gomock.Eq(ctx), gomock.Eq(cdc), gomock.Eq(genesisData["module1"])).Times(1).Return(nil)
require.Panics(t, func() { mm.InitGenesis(ctx, cdc, genesisData) })
_, err := mm.InitGenesis(ctx, cdc, genesisData)
require.ErrorContains(t, err, "validator set is empty after InitGenesis")

// test panic
genesisData = map[string]json.RawMessage{
Expand All @@ -206,14 +207,15 @@ func TestManager_InitGenesis(t *testing.T) {
// panic because more than one module returns validator set updates
mockAppModule1.EXPECT().InitGenesis(gomock.Eq(ctx), gomock.Eq(cdc), gomock.Eq(genesisData["module1"])).Times(1).Return([]abci.ValidatorUpdate{{}})
mockAppModule2.EXPECT().InitGenesis(gomock.Eq(ctx), gomock.Eq(cdc), gomock.Eq(genesisData["module2"])).Times(1).Return([]abci.ValidatorUpdate{{}})
_, err := mm.InitGenesis(ctx, cdc, genesisData)
require.Error(t, err)
_, err = mm.InitGenesis(ctx, cdc, genesisData)
require.ErrorContains(t, err, "validator InitGenesis updates already set by a previous module")

// happy path
mockAppModule1.EXPECT().InitGenesis(gomock.Eq(ctx), gomock.Eq(cdc), gomock.Eq(genesisData["module1"])).Times(1).Return([]abci.ValidatorUpdate{{}})
mockAppModule2.EXPECT().InitGenesis(gomock.Eq(ctx), gomock.Eq(cdc), gomock.Eq(genesisData["module2"])).Times(1).Return([]abci.ValidatorUpdate{})
mockAppModule3.EXPECT().InitGenesis(gomock.Eq(ctx), gomock.Any()).Times(1).Return(nil)
mm.InitGenesis(ctx, cdc, genesisData)
_, err = mm.InitGenesis(ctx, cdc, genesisData)
require.NoError(t, err)
}

func TestManager_ExportGenesis(t *testing.T) {
Expand Down Expand Up @@ -279,7 +281,8 @@ func TestManager_BeginBlock(t *testing.T) {

mockAppModule1.EXPECT().BeginBlock(gomock.Any(), gomock.Eq(req)).Times(1)
mockAppModule2.EXPECT().BeginBlock(gomock.Any(), gomock.Eq(req)).Times(1)
mm.BeginBlock(sdk.Context{}, req)
_, err := mm.BeginBlock(sdk.Context{}, req)
require.NoError(t, err)
}

func TestManager_EndBlock(t *testing.T) {
Expand All @@ -288,11 +291,13 @@ func TestManager_EndBlock(t *testing.T) {

mockAppModule1 := mock.NewMockEndBlockAppModule(mockCtrl)
mockAppModule2 := mock.NewMockEndBlockAppModule(mockCtrl)
mockAppModule3 := mock.NewMockAppModule(mockCtrl)
mockAppModule1.EXPECT().Name().Times(2).Return("module1")
mockAppModule2.EXPECT().Name().Times(2).Return("module2")
mm := module.NewManager(mockAppModule1, mockAppModule2)
mockAppModule3.EXPECT().Name().Times(2).Return("module3")
mm := module.NewManager(mockAppModule1, mockAppModule2, mockAppModule3)
require.NotNil(t, mm)
require.Equal(t, 2, len(mm.Modules))
require.Equal(t, 3, len(mm.Modules))

req := abci.RequestEndBlock{Height: 10}

Expand Down Expand Up @@ -341,9 +346,11 @@ func TestCoreAPIManager_InitGenesis(t *testing.T) {

// this should panic since the validator set is empty even after init genesis
mockAppModule1.EXPECT().InitGenesis(gomock.Eq(ctx), gomock.Any()).Times(1).Return(nil)
require.Panics(t, func() { mm.InitGenesis(ctx, cdc, genesisData) })
_, err := mm.InitGenesis(ctx, cdc, genesisData)
require.ErrorContains(t, err, "validator set is empty after InitGenesis, please ensure at least one validator is initialized with a delegation greater than or equal to the DefaultPowerReduction")

// TODO: add happy path test. We are not returning any validator updates
// TODO: add happy path test. We are not returning any validator updates, this will come with the services.
// REF: https://github.com/cosmos/cosmos-sdk/issues/14688
}

func TestCoreAPIManager_ExportGenesis(t *testing.T) {
Expand Down Expand Up @@ -421,6 +428,60 @@ func TestCoreAPIManagerOrderSetters(t *testing.T) {
require.Equal(t, []string{"module2", "module1", "module3"}, mm.OrderEndBlockers)
}

func TestCoreAPIManager_BeginBlock(t *testing.T) {
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)

mockAppModule1 := mock.NewMockCoreAppModule(mockCtrl)
mockAppModule2 := mock.NewMockCoreAppModule(mockCtrl)
mm := module.NewManagerFromMap(map[string]appmodule.AppModule{
"module1": mockAppModule1,
"module2": mockAppModule2,
})
require.NotNil(t, mm)
require.Equal(t, 2, len(mm.Modules))

req := abci.RequestBeginBlock{Hash: []byte("test")}

mockAppModule1.EXPECT().BeginBlock(gomock.Any()).Times(1).Return(nil)
mockAppModule2.EXPECT().BeginBlock(gomock.Any()).Times(1).Return(nil)
_, err := mm.BeginBlock(sdk.Context{}, req)
require.NoError(t, err)

// test panic
mockAppModule1.EXPECT().BeginBlock(gomock.Any()).Times(1).Return(errors.New("some error"))
_, err = mm.BeginBlock(sdk.Context{}, req)
require.EqualError(t, err, "some error")

}

func TestCoreAPIManager_EndBlock(t *testing.T) {
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)

mockAppModule1 := mock.NewMockCoreAppModule(mockCtrl)
mockAppModule2 := mock.NewMockCoreAppModule(mockCtrl)
mm := module.NewManagerFromMap(map[string]appmodule.AppModule{
"module1": mockAppModule1,
"module2": mockAppModule2,
})
require.NotNil(t, mm)
require.Equal(t, 2, len(mm.Modules))

req := abci.RequestEndBlock{Height: 10}

mockAppModule1.EXPECT().EndBlock(gomock.Any()).Times(1).Return(nil)
mockAppModule2.EXPECT().EndBlock(gomock.Any()).Times(1).Return(nil)
res, err := mm.EndBlock(sdk.Context{}, req)
require.NoError(t, err)
require.Len(t, res.ValidatorUpdates, 0)

// test panic
mockAppModule1.EXPECT().EndBlock(gomock.Any()).Times(1).Return(errors.New("some error"))
_, err = mm.EndBlock(sdk.Context{}, req)
require.EqualError(t, err, "some error")
}

// MockCoreAppModule allows us to test functions like DefaultGenesis
type MockCoreAppModule struct{}

Expand Down

0 comments on commit e36572d

Please sign in to comment.