Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

InitGenesis in migrations when fromVersion==0 #9007

Merged
merged 14 commits into from
Apr 2, 2021
2 changes: 1 addition & 1 deletion simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ func NewSimApp(

app.mm.RegisterInvariants(&app.CrisisKeeper)
app.mm.RegisterRoutes(app.Router(), app.QueryRouter(), encodingConfig.Amino)
app.configurator = module.NewConfigurator(app.MsgServiceRouter(), app.GRPCQueryRouter())
app.configurator = module.NewConfigurator(app.appCodec, app.MsgServiceRouter(), app.GRPCQueryRouter())
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the Configurator/module.Manager refactor, this would be the only place that might need to be updated by app developers (or maybe even not). The Configurator interface itself is not modified for now, to allow more flexibility on our side.

app.mm.RegisterServices(app.configurator)

// add test gRPC service for testing gRPC queries in isolation
Expand Down
51 changes: 50 additions & 1 deletion simapp/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,21 @@ import (
"os"
"testing"

"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/log"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
dbm "github.com/tendermint/tm-db"

"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/tests/mocks"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/auth/vesting"
"github.com/cosmos/cosmos-sdk/x/authz"
"github.com/cosmos/cosmos-sdk/x/bank"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/cosmos/cosmos-sdk/x/capability"
"github.com/cosmos/cosmos-sdk/x/crisis"
Expand Down Expand Up @@ -80,7 +83,7 @@ func TestRunMigrations(t *testing.T) {
bApp.SetCommitMultiStoreTracer(nil)
bApp.SetInterfaceRegistry(encCfg.InterfaceRegistry)
app.BaseApp = bApp
app.configurator = module.NewConfigurator(app.MsgServiceRouter(), app.GRPCQueryRouter())
app.configurator = module.NewConfigurator(app.appCodec, app.MsgServiceRouter(), app.GRPCQueryRouter())

// We register all modules on the Configurator, except x/bank. x/bank will
// serve as the test subject on which we run the migration tests.
Expand Down Expand Up @@ -185,12 +188,58 @@ func TestRunMigrations(t *testing.T) {
require.EqualError(t, err, tc.expRunErrMsg)
} else {
require.NoError(t, err)
// Make sure bank's migration is called.
require.Equal(t, tc.expCalled, called)
}
})
}
}

func TestInitGenesisOnMigration(t *testing.T) {
db := dbm.NewMemDB()
encCfg := MakeTestEncodingConfig()
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
app := NewSimApp(logger, db, nil, true, map[int64]bool{}, DefaultNodeHome, 0, encCfg, EmptyAppOptions{})
ctx := app.NewContext(true, tmproto.Header{Height: app.LastBlockHeight()})

// Create a mock module. This module will serve as the new module we're
// adding during a migration.
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)
mockModule := mocks.NewMockAppModule(mockCtrl)
mockDefaultGenesis := json.RawMessage(`{"key": "value"}`)
mockModule.EXPECT().DefaultGenesis(gomock.Eq(app.appCodec)).Times(1).Return(mockDefaultGenesis)
mockModule.EXPECT().InitGenesis(gomock.Eq(ctx), gomock.Eq(app.appCodec), gomock.Eq(mockDefaultGenesis)).Times(1).Return(nil)

app.mm.Modules["mock"] = mockModule

// Run migrations only for "mock" module. That's why we put the initial
// version for bank as 0 (to run its InitGenesis), and for all other
// modules, we put their latest ConsensusVersion to skip migrations.
_, err := app.RunMigrations(ctx,
module.VersionMap{
"mock": 0,
"bank": bank.AppModule{}.ConsensusVersion(),
"auth": auth.AppModule{}.ConsensusVersion(),
"authz": authz.AppModule{}.ConsensusVersion(),
"staking": staking.AppModule{}.ConsensusVersion(),
"mint": mint.AppModule{}.ConsensusVersion(),
"distribution": distribution.AppModule{}.ConsensusVersion(),
"slashing": slashing.AppModule{}.ConsensusVersion(),
"gov": gov.AppModule{}.ConsensusVersion(),
"params": params.AppModule{}.ConsensusVersion(),
"upgrade": upgrade.AppModule{}.ConsensusVersion(),
"vesting": vesting.AppModule{}.ConsensusVersion(),
"feegrant": feegrant.AppModule{}.ConsensusVersion(),
"evidence": evidence.AppModule{}.ConsensusVersion(),
"crisis": crisis.AppModule{}.ConsensusVersion(),
"genutil": genutil.AppModule{}.ConsensusVersion(),
"capability": capability.AppModule{}.ConsensusVersion(),
},
)
require.NoError(t, err)
}

func TestUpgradeStateOnGenesis(t *testing.T) {
encCfg := MakeTestEncodingConfig()
db := dbm.NewMemDB()
Expand Down
14 changes: 13 additions & 1 deletion types/module/configurator.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package module
import (
"github.com/gogo/protobuf/grpc"

"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
Expand Down Expand Up @@ -31,9 +32,14 @@ type Configurator interface {
// will panic. If the ConsensusVersion bump does not introduce any store
// changes, then a no-op function must be registered here.
RegisterMigration(moduleName string, forVersion uint64, handler MigrationHandler) error

// Cdc defines the app-wide codec interface used for serialization and
// deserialization.
Cdc() codec.Marshaler
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
}

type configurator struct {
cdc codec.Marshaler
msgServer grpc.Server
queryServer grpc.Server

Expand All @@ -42,8 +48,9 @@ type configurator struct {
}

// NewConfigurator returns a new Configurator instance
func NewConfigurator(msgServer grpc.Server, queryServer grpc.Server) Configurator {
func NewConfigurator(cdc codec.Marshaler, msgServer grpc.Server, queryServer grpc.Server) Configurator {
return configurator{
cdc: cdc,
msgServer: msgServer,
queryServer: queryServer,
migrations: map[string]map[uint64]MigrationHandler{},
Expand All @@ -62,6 +69,11 @@ func (c configurator) QueryServer() grpc.Server {
return c.queryServer
}

// Cdc implements the Configurator.Cdc method
func (c configurator) Cdc() codec.Marshaler {
return c.cdc
}

// RegisterMigration implements the Configurator.RegisterMigration method
func (c configurator) RegisterMigration(moduleName string, forVersion uint64, handler MigrationHandler) error {
if forVersion == 0 {
Expand Down
18 changes: 16 additions & 2 deletions types/module/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -349,13 +349,27 @@ func (m Manager) RunMigrations(ctx sdk.Context, cfg Configurator, fromVM Version
fromVersion := fromVM[moduleName]
toVersion := module.ConsensusVersion()

// only run migrations when the from version is > 0
// from version will be 0 when a new module is added and migrations shouldn't be run in this case
// Only run migrations when the fromVersion is > 0, or run InitGenesis
// if fromVersion == 0.
//
// fromVersion will be 0 in two cases:
// 1. If a new module is added. In this case we run InitGenesis with an
// empty genesis state.
// 2. If the app developer is running in-place store migrations for the
// first time. In this case, it is the app developer's responsibility
// to set their module's fromVersions to a version that suits them.
if fromVersion > 0 {
err := c.runModuleMigrations(ctx, moduleName, fromVersion, toVersion)
if err != nil {
return nil, err
}
} else {
moduleValUpdates := module.InitGenesis(ctx, cfg.Cdc(), module.DefaultGenesis(cfg.Cdc()))
// The module manager assumes only one module will update the
// validator set, and that it will not be by a new module.
if len(moduleValUpdates) > 0 {
return nil, sdkerrors.Wrapf(sdkerrors.ErrLogic, "validator InitGenesis updates already set by a previous module")
}
}

updatedVM[moduleName] = toVersion
Expand Down
4 changes: 3 additions & 1 deletion types/module/module_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,9 @@ func TestManager_RegisterQueryServices(t *testing.T) {

msgRouter := mocks.NewMockServer(mockCtrl)
queryRouter := mocks.NewMockServer(mockCtrl)
cfg := module.NewConfigurator(msgRouter, queryRouter)
interfaceRegistry := types.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(interfaceRegistry)
cfg := module.NewConfigurator(cdc, msgRouter, queryRouter)
mockAppModule1.EXPECT().RegisterServices(cfg).Times(1)
mockAppModule2.EXPECT().RegisterServices(cfg).Times(1)

Expand Down