refactor: introduce abciGenesis interface (#17554)
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user