Skip to content

Commit

Permalink
docs(app/module): README (#3483)
Browse files Browse the repository at this point in the history
  • Loading branch information
rootulp authored May 15, 2024
1 parent 82f26b8 commit 06b4c96
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 135 deletions.
44 changes: 20 additions & 24 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,13 @@ type App struct {
ScopedTransferKeeper capabilitykeeper.ScopedKeeper // This keeper is public for test purposes
ScopedICAHostKeeper capabilitykeeper.ScopedKeeper // This keeper is public for test purposes

mm *module.Manager
manager *module.Manager
configurator module.Configurator
// upgradeHeightV2 is used as a coordination mechanism for the height-based
// upgrade from v1 to v2.
upgradeHeightV2 int64
// used to define what messages are accepted for a given app version
// MsgGateKeeper is used to define which messages are accepted for a given
// app version.
MsgGateKeeper *ante.MsgVersioningGateKeeper
}

Expand All @@ -187,20 +188,19 @@ func New(
baseAppOptions ...func(*baseapp.BaseApp),
) *App {
appCodec := encodingConfig.Codec
cdc := encodingConfig.Amino
interfaceRegistry := encodingConfig.InterfaceRegistry

bApp := baseapp.NewBaseApp(Name, logger, db, encodingConfig.TxConfig.TxDecoder(), baseAppOptions...)
bApp.SetCommitMultiStoreTracer(traceStore)
bApp.SetVersion(version.Version)
bApp.SetInterfaceRegistry(interfaceRegistry)
baseApp := baseapp.NewBaseApp(Name, logger, db, encodingConfig.TxConfig.TxDecoder(), baseAppOptions...)
baseApp.SetCommitMultiStoreTracer(traceStore)
baseApp.SetVersion(version.Version)
baseApp.SetInterfaceRegistry(interfaceRegistry)

keys := sdk.NewKVStoreKeys(allStoreKeys()...)
tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey)
memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey)

app := &App{
BaseApp: bApp,
BaseApp: baseApp,
appCodec: appCodec,
interfaceRegistry: interfaceRegistry,
txConfig: encodingConfig.TxConfig,
Expand All @@ -212,28 +212,24 @@ func New(
upgradeHeightV2: upgradeHeightV2,
}

app.ParamsKeeper = initParamsKeeper(appCodec, cdc, keys[paramstypes.StoreKey], tkeys[paramstypes.TStoreKey])
app.ParamsKeeper = initParamsKeeper(appCodec, encodingConfig.Amino, keys[paramstypes.StoreKey], tkeys[paramstypes.TStoreKey])

// set the BaseApp's parameter store
bApp.SetParamStore(app.ParamsKeeper.Subspace(baseapp.Paramspace).WithKeyTable(paramstypes.ConsensusParamsKeyTable()))
baseApp.SetParamStore(app.ParamsKeeper.Subspace(baseapp.Paramspace).WithKeyTable(paramstypes.ConsensusParamsKeyTable()))

// add capability keeper and ScopeToModule for ibc module
app.CapabilityKeeper = capabilitykeeper.NewKeeper(appCodec, keys[capabilitytypes.StoreKey], memKeys[capabilitytypes.MemStoreKey])

// grant capabilities for the ibc and ibc-transfer modules
app.ScopedIBCKeeper = app.CapabilityKeeper.ScopeToModule(ibchost.ModuleName)
app.ScopedTransferKeeper = app.CapabilityKeeper.ScopeToModule(ibctransfertypes.ModuleName)
app.ScopedICAHostKeeper = app.CapabilityKeeper.ScopeToModule(icahosttypes.SubModuleName)

// add keepers
app.AccountKeeper = authkeeper.NewAccountKeeper(
appCodec, keys[authtypes.StoreKey], app.GetSubspace(authtypes.ModuleName), authtypes.ProtoBaseAccount, maccPerms, sdk.GetConfig().GetBech32AccountAddrPrefix(),
)
app.BankKeeper = bankkeeper.NewBaseKeeper(
appCodec, keys[banktypes.StoreKey], app.AccountKeeper, app.GetSubspace(banktypes.ModuleName), app.ModuleAccountAddrs(),
)
app.AuthzKeeper = authzkeeper.NewKeeper(
keys[authzkeeper.StoreKey], appCodec, bApp.MsgServiceRouter(), app.AccountKeeper,
keys[authzkeeper.StoreKey], appCodec, baseApp.MsgServiceRouter(), app.AccountKeeper,
)
stakingKeeper := stakingkeeper.NewKeeper(
appCodec, keys[stakingtypes.StoreKey], app.AccountKeeper, app.BankKeeper, app.GetSubspace(stakingtypes.ModuleName),
Expand Down Expand Up @@ -364,7 +360,7 @@ func New(
app.BankKeeper,
&stakingKeeper,
govRouter,
bApp.MsgServiceRouter(),
baseApp.MsgServiceRouter(),
govtypes.DefaultConfig(),
)

Expand Down Expand Up @@ -397,9 +393,9 @@ func New(
app.QueryRouter().AddRoute(proof.TxInclusionQueryPath, proof.QueryTxInclusionProof)
app.QueryRouter().AddRoute(proof.ShareInclusionQueryPath, proof.QueryShareInclusionProof)

app.mm.RegisterInvariants(&app.CrisisKeeper)
app.manager.RegisterInvariants(&app.CrisisKeeper)
app.configurator = module.NewConfigurator(app.appCodec, app.MsgServiceRouter(), app.GRPCQueryRouter())
app.mm.RegisterServices(app.configurator)
app.manager.RegisterServices(app.configurator)

// extract the accepted message list from the configurator and create a gatekeeper
// which will be used both as the antehandler and as part of the circuit breaker in
Expand Down Expand Up @@ -448,12 +444,12 @@ func (app *App) Name() string { return app.BaseApp.Name() }

// BeginBlocker application updates every begin block
func (app *App) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
return app.mm.BeginBlock(ctx, req)
return app.manager.BeginBlock(ctx, req)
}

// EndBlocker executes application updates at the end of every block.
func (app *App) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
res := app.mm.EndBlock(ctx, req)
res := app.manager.EndBlock(ctx, req)
currentVersion := app.AppVersion()
// For v1 only we upgrade using a agreed upon height known ahead of time
if currentVersion == v1 {
Expand Down Expand Up @@ -503,7 +499,7 @@ func (app *App) migrateCommitStore(fromVersion, toVersion uint64) (baseapp.Store
// migrateModules performs migrations on existing modules that have registered migrations
// between versions and initializes the state of new modules for the specified app version.
func (app *App) migrateModules(ctx sdk.Context, fromVersion, toVersion uint64) error {
return app.mm.RunMigrations(ctx, app.configurator, fromVersion, toVersion)
return app.manager.RunMigrations(ctx, app.configurator, fromVersion, toVersion)
}

// Info implements the ABCI interface. This method is a wrapper around baseapp's
Expand Down Expand Up @@ -583,8 +579,8 @@ func (app *App) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.Res
panic(err)
}

app.UpgradeKeeper.SetModuleVersionMap(ctx, app.mm.GetVersionMap(req.ConsensusParams.Version.AppVersion))
return app.mm.InitGenesis(ctx, app.appCodec, genesisState, req.ConsensusParams.Version.AppVersion)
app.UpgradeKeeper.SetModuleVersionMap(ctx, app.manager.GetVersionMap(req.ConsensusParams.Version.AppVersion))
return app.manager.InitGenesis(ctx, app.appCodec, genesisState, req.ConsensusParams.Version.AppVersion)
}

// LoadHeight loads a particular height
Expand All @@ -595,7 +591,7 @@ func (app *App) LoadHeight(height int64) error {
// SupportedVersions returns all the state machines that the
// application supports
func (app *App) SupportedVersions() []uint64 {
return app.mm.SupportedVersions()
return app.manager.SupportedVersions()
}

// versionedKeys returns a map from moduleName to KV store key for the given app
Expand Down
2 changes: 1 addition & 1 deletion app/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func (app *App) ExportAppStateAndValidators(forZeroHeight bool, jailAllowedAddrs
app.prepForZeroHeightGenesis(ctx, jailAllowedAddrs)
}

genState := app.mm.ExportGenesis(ctx, app.appCodec, app.AppVersion())
genState := app.manager.ExportGenesis(ctx, app.appCodec, app.AppVersion())
appState, err := json.MarshalIndent(genState, "", " ")
if err != nil {
return servertypes.ExportedApp{}, err
Expand Down
3 changes: 3 additions & 0 deletions app/module/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# app/module

This directory contains a module manager (`Manager`) and configurator (`Configurator`) that enables the application to add or remove modules during app version changes.
5 changes: 3 additions & 2 deletions app/module/configurator.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/cosmos/cosmos-sdk/types/module"
)

// Configurator implements the module.Configurator interface.
var _ module.Configurator = Configurator{}

// Configurator is a struct used at startup to register all the message and
Expand Down Expand Up @@ -60,12 +61,12 @@ func (c Configurator) GetAcceptedMessages() map[uint64]map[string]struct{} {
return c.acceptedMessages
}

// QueryServer implements the Configurator.QueryServer method
// QueryServer implements the Configurator.QueryServer method.
func (c Configurator) QueryServer() pbgrpc.Server {
return c.queryServer
}

// RegisterMigration implements the Configurator.RegisterMigration method
// RegisterMigration implements the Configurator.RegisterMigration method.
func (c Configurator) RegisterMigration(moduleName string, fromVersion uint64, handler module.MigrationHandler) error {
if fromVersion == 0 {
return sdkerrors.ErrInvalidVersion.Wrap("module migration versions should start at 1")
Expand Down
111 changes: 84 additions & 27 deletions app/module/configurator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,95 @@ import (
"github.com/cosmos/cosmos-sdk/tests/mocks"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/libs/log"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
dbm "github.com/tendermint/tm-db"
)

func TestConfiguratorRegistersAllMessageTypes(t *testing.T) {
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)
mockServer := mocks.NewMockServer(mockCtrl)
mockServer.EXPECT().RegisterService(gomock.Any(), gomock.Any()).Times(2).Return()
cdc := encoding.MakeConfig(app.ModuleEncodingRegisters...)
configurator := module.NewConfigurator(cdc.Codec, mockServer, mockServer)

storeKey := sdk.NewKVStoreKey(signaltypes.StoreKey)

db := dbm.NewMemDB()
stateStore := store.NewCommitMultiStore(db)
stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db)
require.NoError(t, stateStore.LoadLatestVersion())

keeper := signal.NewKeeper(storeKey, nil)
upgradeModule := signal.NewAppModule(keeper)
mm, err := module.NewManager([]module.VersionedModule{
{Module: upgradeModule, FromVersion: 2, ToVersion: 2},
func TestConfigurator(t *testing.T) {
t.Run("registers all accepted messages", func(t *testing.T) {
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)

mockServer := mocks.NewMockServer(mockCtrl)
mockServer.EXPECT().RegisterService(gomock.Any(), gomock.Any()).Times(2).Return()

config := encoding.MakeConfig(app.ModuleEncodingRegisters...)
configurator := module.NewConfigurator(config.Codec, mockServer, mockServer)
storeKey := sdk.NewKVStoreKey(signaltypes.StoreKey)

db := dbm.NewMemDB()
stateStore := store.NewCommitMultiStore(db)
stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db)
require.NoError(t, stateStore.LoadLatestVersion())

keeper := signal.NewKeeper(storeKey, nil)
require.NotNil(t, keeper)
upgradeModule := signal.NewAppModule(keeper)
manager, err := module.NewManager([]module.VersionedModule{
{Module: upgradeModule, FromVersion: 2, ToVersion: 2},
})
require.NoError(t, err)
require.NotNil(t, manager)

manager.RegisterServices(configurator)
acceptedMessages := configurator.GetAcceptedMessages()
assert.Equal(t, map[uint64]map[string]struct{}{
2: {
"/celestia.signal.v1.MsgSignalVersion": {},
"/celestia.signal.v1.MsgTryUpgrade": {},
},
}, acceptedMessages)
})
require.NoError(t, err)
require.NotNil(t, mm)

mm.RegisterServices(configurator)
acceptedMessages := configurator.GetAcceptedMessages()
require.Equal(t, map[uint64]map[string]struct{}{
2: {"/celestia.signal.v1.MsgSignalVersion": {}, "/celestia.signal.v1.MsgTryUpgrade": {}},
}, acceptedMessages)
t.Run("register migration", func(t *testing.T) {
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)

mockAppModule1 := mocks.NewMockAppModule(mockCtrl)
mockAppModule2 := mocks.NewMockAppModule(mockCtrl)
mockAppModule3 := mocks.NewMockAppModule(mockCtrl)

require.NotNil(t, keeper)
mockAppModule1.EXPECT().Name().Return("testModule").AnyTimes()
mockAppModule2.EXPECT().Name().Return("testModule").AnyTimes()
mockAppModule3.EXPECT().Name().Return("differentModule").AnyTimes()
mockAppModule1.EXPECT().ConsensusVersion().Return(uint64(1)).AnyTimes()
mockAppModule2.EXPECT().ConsensusVersion().Return(uint64(2)).AnyTimes()
mockAppModule3.EXPECT().ConsensusVersion().Return(uint64(5)).AnyTimes()
mockAppModule3.EXPECT().InitGenesis(gomock.Any(), gomock.Any(), gomock.Any()).Times(1).Return(nil)
mockAppModule3.EXPECT().DefaultGenesis(gomock.Any()).Return(nil)

manager, err := module.NewManager([]module.VersionedModule{
// this is an existing module that gets updated in v2
{Module: mockAppModule1, FromVersion: 1, ToVersion: 1},
{Module: mockAppModule2, FromVersion: 2, ToVersion: 3},
// This is a new module that gets added in v2
{Module: mockAppModule3, FromVersion: 2, ToVersion: 2},
})
require.NoError(t, err)
require.NotNil(t, manager)

mockServer := mocks.NewMockServer(mockCtrl)
config := encoding.MakeConfig(app.ModuleEncodingRegisters...)

isCalled := false
configurator := module.NewConfigurator(config.Codec, mockServer, mockServer)
err = configurator.RegisterMigration("testModule", 1, func(_ sdk.Context) error {
isCalled = true
return nil
})
require.NoError(t, err)

err = manager.RunMigrations(sdk.NewContext(nil, tmproto.Header{}, false, log.NewNopLogger()), configurator, 1, 2)
require.NoError(t, err)
require.True(t, isCalled)

supportedVersions := manager.SupportedVersions()
require.Len(t, supportedVersions, 3)
require.Contains(t, supportedVersions, uint64(1))
require.Contains(t, supportedVersions, uint64(2))
require.Contains(t, supportedVersions, uint64(3))
})
}
26 changes: 14 additions & 12 deletions app/module/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import (
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

// Manager defines a module manager that provides the high level utility for managing and executing
// operations for a group of modules. This implemention maps the state machine version to different
// versions of the module. It also provides a way to run migrations between different versions of a
// module.
// Manager defines a module manager that provides the high level utility for
// managing and executing operations for a group of modules. This implementation
// was originally inspired by the module manager defined in Cosmos SDK but this
// implemention maps the state machine version to different versions of the
// module. It also provides a way to run migrations between different versions
// of a module.
type Manager struct {
// versionedModules is a map from app version -> module name -> module.
versionedModules map[uint64]map[string]sdkmodule.AppModule
Expand All @@ -39,7 +41,7 @@ type Manager struct {

// NewManager returns a new Manager object.
func NewManager(modules []VersionedModule) (*Manager, error) {
moduleMap := make(map[uint64]map[string]sdkmodule.AppModule)
versionedModules := make(map[uint64]map[string]sdkmodule.AppModule)
allModules := make([]sdkmodule.AppModule, len(modules))
modulesStr := make([]string, 0, len(modules))
uniqueModuleVersions := make(map[string]map[uint64][2]uint64)
Expand All @@ -53,13 +55,13 @@ func NewManager(modules []VersionedModule) (*Manager, error) {
return nil, sdkerrors.ErrLogic.Wrapf("FromVersion cannot be greater than ToVersion for module %s", module.Module.Name())
}
for version := module.FromVersion; version <= module.ToVersion; version++ {
if moduleMap[version] == nil {
moduleMap[version] = make(map[string]sdkmodule.AppModule)
if versionedModules[version] == nil {
versionedModules[version] = make(map[string]sdkmodule.AppModule)
}
if _, exists := moduleMap[version][name]; exists {
if _, exists := versionedModules[version][name]; exists {
return nil, sdkerrors.ErrLogic.Wrapf("Two different modules with domain %s are registered with the same version %d", name, version)
}
moduleMap[version][module.Module.Name()] = module.Module
versionedModules[version][module.Module.Name()] = module.Module
}
allModules[idx] = module.Module
modulesStr = append(modulesStr, name)
Expand All @@ -68,11 +70,11 @@ func NewManager(modules []VersionedModule) (*Manager, error) {
}
uniqueModuleVersions[name][moduleVersion] = [2]uint64{module.FromVersion, module.ToVersion}
}
firstVersion := slices.Min(getKeys(moduleMap))
lastVersion := slices.Max(getKeys(moduleMap))
firstVersion := slices.Min(getKeys(versionedModules))
lastVersion := slices.Max(getKeys(versionedModules))

m := &Manager{
versionedModules: moduleMap,
versionedModules: versionedModules,
uniqueModuleVersions: uniqueModuleVersions,
allModules: allModules,
firstVersion: firstVersion,
Expand Down
Loading

0 comments on commit 06b4c96

Please sign in to comment.