refactor: introduce abciGenesis interface (#17554)

This commit is contained in:
Marko
2023-08-29 15:24:09 +00:00
committed by GitHub
parent 0a253f3c6d
commit 6ce2505f6b
35 changed files with 561 additions and 508 deletions
+5 -2
View File
@@ -19,7 +19,7 @@ import (
var (
_ AppModuleBasic = coreAppModuleBasicAdaptor{}
_ HasGenesis = coreAppModuleBasicAdaptor{}
_ HasABCIGenesis = coreAppModuleBasicAdaptor{}
_ HasServices = coreAppModuleBasicAdaptor{}
)
@@ -122,9 +122,12 @@ func (c coreAppModuleBasicAdaptor) InitGenesis(ctx sdk.Context, cdc codec.JSONCo
}
if mod, ok := c.module.(HasGenesis); ok {
mod.InitGenesis(ctx, cdc, bz)
return nil
}
if mod, ok := c.module.(HasABCIGenesis); ok {
return mod.InitGenesis(ctx, cdc, bz)
}
return nil
}
+11
View File
@@ -18,6 +18,17 @@ type AppModuleWithAllExtensions interface {
module.HasName
}
// mocks to be used in module tests.
type AppModuleWithAllExtensionsABCI interface {
module.AppModule
module.HasServices
module.HasABCIGenesis
module.HasInvariants
module.HasConsensusVersion
module.HasABCIEndblock
module.HasName
}
// CoreAppModule is solely here for the purpose of generating
// mocks to be used in module tests.
type CoreAppModule interface {
+45 -27
View File
@@ -2,7 +2,6 @@
Package module contains application module patterns and associated "manager" functionality.
The module pattern has been broken down by:
- independent module functionality (AppModuleBasic)
- inter-dependent module genesis functionality (AppModuleGenesis)
- inter-dependent module simulation functionality (AppModuleSimulation)
- inter-dependent module full functionality (AppModule)
@@ -21,7 +20,7 @@ have to manually register all of the codecs for all the modules. This basic
procedure as well as other basic patterns are handled through the use of
BasicManager.
Lastly the interface for genesis functionality (AppModuleGenesis) has been
Lastly the interface for genesis functionality (HasGenesis & HasABCIGenesis) has been
separated out from full module functionality (AppModule) so that modules which
are only used for genesis can take advantage of the Module patterns without
needlessly defining many placeholder functions
@@ -182,14 +181,15 @@ func (bm BasicManager) AddQueryCommands(rootQueryCmd *cobra.Command) {
}
}
// AppModuleGenesis is the standard form for an application module genesis functions
type AppModuleGenesis interface {
AppModuleBasic
HasGenesis
}
// HasGenesis is the extension interface for stateful genesis methods.
type HasGenesis interface {
HasGenesisBasics
InitGenesis(sdk.Context, codec.JSONCodec, json.RawMessage)
ExportGenesis(sdk.Context, codec.JSONCodec) json.RawMessage
}
// HasABCIGenesis is the extension interface for stateful genesis methods which returns validator updates.
type HasABCIGenesis interface {
HasGenesisBasics
InitGenesis(sdk.Context, codec.JSONCodec, json.RawMessage) []abci.ValidatorUpdate
ExportGenesis(sdk.Context, codec.JSONCodec) json.RawMessage
@@ -227,15 +227,26 @@ type HasABCIEndblock interface {
EndBlock(context.Context) ([]abci.ValidatorUpdate, error)
}
var (
_ appmodule.AppModule = (*GenesisOnlyAppModule)(nil)
_ AppModuleBasic = (*GenesisOnlyAppModule)(nil)
)
// genesisOnlyModule is an interface need to return GenesisOnlyAppModule struct in order to wrap two interfaces
type genesisOnlyModule interface {
AppModuleBasic
HasABCIGenesis
}
// GenesisOnlyAppModule is an AppModule that only has import/export functionality
type GenesisOnlyAppModule struct {
AppModuleGenesis
genesisOnlyModule
}
// NewGenesisOnlyAppModule creates a new GenesisOnlyAppModule object
func NewGenesisOnlyAppModule(amg AppModuleGenesis) GenesisOnlyAppModule {
func NewGenesisOnlyAppModule(amg genesisOnlyModule) GenesisOnlyAppModule {
return GenesisOnlyAppModule{
AppModuleGenesis: amg,
genesisOnlyModule: amg,
}
}
@@ -248,23 +259,9 @@ func (GenesisOnlyAppModule) IsAppModule() {}
// RegisterInvariants is a placeholder function register no invariants
func (GenesisOnlyAppModule) RegisterInvariants(_ sdk.InvariantRegistry) {}
// QuerierRoute returns an empty module querier route
func (GenesisOnlyAppModule) QuerierRoute() string { return "" }
// RegisterServices registers all services.
func (gam GenesisOnlyAppModule) RegisterServices(Configurator) {}
// ConsensusVersion implements AppModule/ConsensusVersion.
func (gam GenesisOnlyAppModule) ConsensusVersion() uint64 { return 1 }
// BeginBlock returns an empty module begin-block
func (gam GenesisOnlyAppModule) BeginBlock(ctx sdk.Context) error { return nil }
// EndBlock returns an empty module end-block
func (GenesisOnlyAppModule) EndBlock(sdk.Context) ([]abci.ValidatorUpdate, error) {
return []abci.ValidatorUpdate{}, nil
}
// Manager defines a module manager that provides the high level utility for managing and executing
// operations for a group of modules
type Manager struct {
@@ -330,6 +327,10 @@ func (m *Manager) SetOrderInitGenesis(moduleNames ...string) {
return !hasGenesis
}
if _, hasABCIGenesis := module.(HasABCIGenesis); hasABCIGenesis {
return !hasABCIGenesis
}
_, hasGenesis := module.(HasGenesis)
return !hasGenesis
})
@@ -344,6 +345,10 @@ func (m *Manager) SetOrderExportGenesis(moduleNames ...string) {
return !hasGenesis
}
if _, hasABCIGenesis := module.(HasABCIGenesis); hasABCIGenesis {
return !hasABCIGenesis
}
_, hasGenesis := module.(HasGenesis)
return !hasGenesis
})
@@ -462,6 +467,9 @@ func (m *Manager) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, genesisData
return &abci.ResponseInitChain{}, err
}
} else if module, ok := mod.(HasGenesis); ok {
ctx.Logger().Debug("running initialization for module", "module", moduleName)
module.InitGenesis(ctx, cdc, genesisData[moduleName])
} else if module, ok := mod.(HasABCIGenesis); ok {
ctx.Logger().Debug("running initialization for module", "module", moduleName)
moduleValUpdates := module.InitGenesis(ctx, cdc, genesisData[moduleName])
@@ -535,6 +543,12 @@ func (m *Manager) ExportGenesisForModules(ctx sdk.Context, cdc codec.JSONCodec,
ctx := ctx.WithGasMeter(storetypes.NewInfiniteGasMeter()) // avoid race conditions
ch <- genesisResult{module.ExportGenesis(ctx, cdc), nil}
}(module, channels[moduleName])
} else if module, ok := mod.(HasABCIGenesis); ok {
channels[moduleName] = make(chan genesisResult)
go func(module HasABCIGenesis, ch chan genesisResult) {
ctx := ctx.WithGasMeter(storetypes.NewInfiniteGasMeter()) // avoid race conditions
ch <- genesisResult{module.ExportGenesis(ctx, cdc), nil}
}(module, channels[moduleName])
}
}
@@ -680,8 +694,12 @@ func (m Manager) RunMigrations(ctx context.Context, cfg Configurator, fromVM Ver
}
} else {
sdkCtx.Logger().Info(fmt.Sprintf("adding a new module: %s", moduleName))
if module, ok := m.Modules[moduleName].(HasGenesis); ok {
moduleValUpdates := module.InitGenesis(sdkCtx, c.cdc, module.DefaultGenesis(c.cdc))
module1, ok := m.Modules[moduleName].(HasGenesis)
if ok {
module1.InitGenesis(sdkCtx, c.cdc, module1.DefaultGenesis(c.cdc))
}
if module2, ok := m.Modules[moduleName].(HasABCIGenesis); ok {
moduleValUpdates := module2.InitGenesis(sdkCtx, c.cdc, module1.DefaultGenesis(c.cdc))
// The module manager assumes only one module will update the
// validator set, and it can't be a new module.
if len(moduleValUpdates) > 0 {
+15 -20
View File
@@ -92,18 +92,6 @@ func TestBasicManager(t *testing.T) {
require.Nil(t, module.NewBasicManager().ValidateGenesis(cdc, nil, expDefaultGenesis))
}
func TestGenesisOnlyAppModule(t *testing.T) {
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)
mockModule := mock.NewMockAppModuleGenesis(mockCtrl)
mockInvariantRegistry := mock.NewMockInvariantRegistry(mockCtrl)
goam := module.NewGenesisOnlyAppModule(mockModule)
// no-op
goam.RegisterInvariants(mockInvariantRegistry)
}
func TestAssertNoForgottenModules(t *testing.T) {
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)
@@ -234,7 +222,7 @@ func TestManager_InitGenesis(t *testing.T) {
mockAppModule2 := mock.NewMockAppModuleWithAllExtensions(mockCtrl)
mockAppModule3 := mock.NewMockCoreAppModule(mockCtrl)
mockAppModule1.EXPECT().Name().Times(2).Return("module1")
mockAppModule2.EXPECT().Name().Times(2).Return("module2")
mockAppModule2.EXPECT().Name().Times(4).Return("module2")
mm := module.NewManager(mockAppModule1, mockAppModule2, module.CoreAppModuleBasicAdaptor("module3", mockAppModule3))
require.NotNil(t, mm)
require.Equal(t, 3, len(mm.Modules))
@@ -245,7 +233,7 @@ func TestManager_InitGenesis(t *testing.T) {
genesisData := map[string]json.RawMessage{"module1": json.RawMessage(`{"key": "value"}`)}
// 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)
mockAppModule1.EXPECT().InitGenesis(gomock.Eq(ctx), gomock.Eq(cdc), gomock.Eq(genesisData["module1"])).Times(1)
_, err := mm.InitGenesis(ctx, cdc, genesisData)
require.ErrorContains(t, err, "validator set is empty after InitGenesis")
@@ -256,17 +244,24 @@ func TestManager_InitGenesis(t *testing.T) {
"module3": json.RawMessage(`{"key": "value"}`),
}
mockAppModuleABCI1 := mock.NewMockAppModuleWithAllExtensionsABCI(mockCtrl)
mockAppModuleABCI2 := mock.NewMockAppModuleWithAllExtensionsABCI(mockCtrl)
mockAppModuleABCI1.EXPECT().Name().Times(4).Return("module1")
mockAppModuleABCI2.EXPECT().Name().Times(2).Return("module2")
mmABCI := module.NewManager(mockAppModuleABCI1, mockAppModuleABCI2)
// 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)
mockAppModuleABCI1.EXPECT().InitGenesis(gomock.Eq(ctx), gomock.Eq(cdc), gomock.Eq(genesisData["module1"])).Times(1).Return([]abci.ValidatorUpdate{{}})
mockAppModuleABCI2.EXPECT().InitGenesis(gomock.Eq(ctx), gomock.Eq(cdc), gomock.Eq(genesisData["module2"])).Times(1).Return([]abci.ValidatorUpdate{{}})
_, err = mmABCI.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{})
mm2 := module.NewManager(mockAppModuleABCI1, mockAppModule2, module.CoreAppModuleBasicAdaptor("module3", mockAppModule3))
mockAppModuleABCI1.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)
mockAppModule3.EXPECT().InitGenesis(gomock.Eq(ctx), gomock.Any()).Times(1).Return(nil)
_, err = mm.InitGenesis(ctx, cdc, genesisData)
_, err = mm2.InitGenesis(ctx, cdc, genesisData)
require.NoError(t, err)
}