From af06f2f65c0e87cbc8b295b5cbfa4e6895121459 Mon Sep 17 00:00:00 2001 From: Robert Zaremba Date: Thu, 25 Nov 2021 00:14:43 +0100 Subject: [PATCH 1/6] fix: always move auth module migration to the end --- types/module/module.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/types/module/module.go b/types/module/module.go index 57b2df544378..0159145b4e18 100644 --- a/types/module/module.go +++ b/types/module/module.go @@ -395,10 +395,19 @@ func (m Manager) RunMigrations(ctx sdk.Context, cfg Configurator, fromVM Version // and the order of executing migrations matters) // TODO: make the order user-configurable? sortedModNames := make([]string, 0, len(m.Modules)) + hasAuth := false + const authModulename = "auth" // using authtypes.ModuleName causes import cycle. for key := range m.Modules { - sortedModNames = append(sortedModNames, key) + if key != authModulename { + sortedModNames = append(sortedModNames, key) + } else { + hasAuth = true + } } sort.Strings(sortedModNames) + if hasAuth { + sortedModNames = append(sortedModNames, authModulename) + } for _, moduleName := range sortedModNames { module := m.Modules[moduleName] From 37f75ce70df58af12ec03d2b9328daa9c16d9669 Mon Sep 17 00:00:00 2001 From: Robert Zaremba Date: Thu, 25 Nov 2021 11:34:12 +0100 Subject: [PATCH 2/6] update migration docs --- docs/core/upgrade.md | 99 +++++++++++++++++++++++++----------------- types/module/module.go | 6 +++ 2 files changed, 65 insertions(+), 40 deletions(-) diff --git a/docs/core/upgrade.md b/docs/core/upgrade.md index fbbba9e1bea3..a92d675c490e 100644 --- a/docs/core/upgrade.md +++ b/docs/core/upgrade.md @@ -15,27 +15,13 @@ The Cosmos SDK uses two methods to perform upgrades. - Exporting the entire application state to a JSON file using the `export` CLI command, making changes, and then starting a new binary with the changed JSON file as the genesis file. See [Chain Upgrade Guide to v0.42](/v0.42/migrations/chain-upgrade-guide-040.html). - Version v0.44 and later can perform upgrades in place to significantly decrease the upgrade time for chains with a larger state. Use the [Module Upgrade Guide](../building-modules/upgrade.md) to set up your application modules to take advantage of in-place upgrades. - + This document provides steps to use the In-Place Store Migrations upgrade method. ## Tracking Module Versions Each module gets assigned a consensus version by the module developer. The consensus version serves as the breaking change version of the module. The SDK keeps track of all module consensus versions in the x/upgrade `VersionMap` store. During an upgrade, the difference between the old `VersionMap` stored in state and the new `VersionMap` is calculated by the Cosmos SDK. For each identified difference, the module-specific migrations are run and the respective consensus version of each upgraded module is incremented. -## Genesis State - -When starting a new chain, the consensus version of each module must be saved to state during the application's genesis. To save the consensus version, add the following line to the `InitChainer` method in `app.go`: - -```diff -func (app *MyApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { - ... -+ app.UpgradeKeeper.SetModuleVersionMap(ctx, app.mm.GetVersionMap()) - ... -} -``` - -This information is used by the Cosmos SDK to detect when modules with newer versions are introduced to the app. - ### Consensus Version The consensus version is defined on each app module by the module developer and serves as the breaking change version of the module. The consensus version informs the SDK on which modules need to be upgraded. For example, if the bank module was version 2 and an upgrade introduces bank module 3, the SDK upgrades the bank module and runs the "version 2 to 3" migration script. @@ -64,20 +50,40 @@ Migrations are run inside of an `UpgradeHandler` using `app.mm.RunMigrations(ct ```go cfg := module.NewConfigurator(...) -app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx sdk.Context, plan upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { +app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { // ... // do upgrade logic // ... - // RunMigrations returns the VersionMap - // with the updated module ConsensusVersions - return app.mm.RunMigrations(ctx, vm) + // returns a VersionMap with the updated module ConsensusVersions + return app.mm.RunMigrations(ctx, fromVM) }) ``` To learn more about configuring migration scripts for your modules, see the [Module Upgrade Guide](../building-modules/upgrade.md). +### Order Of Migrations + +All migrations are run in alphabetical order, except `x/auth` which is run the last. [hack] If you want to change the order of migration then you can run migrations in multiple stages. For example, you want to run `foo` last: + +```go +app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + + fooFrom := fromVM["foo"] + fromVM["foo"] = foo.AppModule{}.ConsensusVersion() + toVM, err := app.mm.RunMigrations(ctx, cfg, fromVM) + if err != nil { + return toVM, err + } + + stage2 := module.VersionMap{"foo": fooFrom} + _, err = app.mm.RunMigrations(ctx, cfg, stage2) + + return toVM, err +}) +``` + ## Adding New Modules During Upgrades You can introduce entirely new modules to the application during an upgrade. New modules are recognized because they have not yet been registered in `x/upgrade`'s `VersionMap` store. In this case, `RunMigrations` calls the `InitGenesis` function from the corresponding module to set up its initial state. @@ -95,7 +101,7 @@ if err != nil { if upgradeInfo.Name == "my-plan" && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) { storeUpgrades := storetypes.StoreUpgrades{ // add store upgrades for new modules - // Example: + // Example: // Added: []string{"foo", "bar"}, // ... } @@ -105,7 +111,35 @@ if upgradeInfo.Name == "my-plan" && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo. } ``` -## Overwriting Genesis Functions +## Genesis State + +When starting a new chain, the consensus version of each module MUST be saved to state during the application's genesis. To save the consensus version, add the following line to the `InitChainer` method in `app.go`: + +```diff +func (app *MyApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { + ... ++ app.UpgradeKeeper.SetModuleVersionMap(ctx, app.mm.GetVersionMap()) + ... +} +``` + +This information is used by the Cosmos SDK to detect when modules with newer versions are introduced to the app. + +For a new module `foo`, `InitGenesis` is called by the `RunMigration` only when there is a new module registered in the module manager and there is no `A` entry in the `fromVM` registered in the state. So, if you don't want to call `InitGenesis` when a new module is added to the app, then you should set zero in the `fromVM` for module `foo`: + +```go +app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + // ... + + // Set foo's version to the latest ConsensusVersion in the VersionMap. + // This will skip running InitGenesis on Foo + fromVM[foo.ModuleName] = foo.AppModule{}.ConsensusVersion() + + return app.mm.RunMigrations(ctx, fromVM) +}) +``` + +### Overwriting Genesis Functions The Cosmos SDK offers modules that the application developer can import in their app. These modules often have an `InitGenesis` function already defined. @@ -118,32 +152,17 @@ You MUST manually set the consensus version in the version map passed to the `Up ```go import foo "github.com/my/module/foo" -app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx sdk.Context, plan upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { - +app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { + // Register the consensus version in the version map // to avoid the SDK from triggering the default // InitGenesis function. - vm["foo"] = foo.AppModule{}.ConsensusVersion() + fromVM["foo"] = foo.AppModule{}.ConsensusVersion() // Run custom InitGenesis for foo app.mm["foo"].InitGenesis(ctx, app.appCodec, myCustomGenesisState) - return app.mm.RunMigrations(ctx, cfg, vm) -}) -``` - -If you do not have a custom genesis function and want to skip the module's default genesis function, you can simply register the module with the version map in the `UpgradeHandler` as shown in the example: - -```go -import foo "github.com/my/module/foo" - -app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx sdk.Context, plan upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { - - // Set foo's version to the latest ConsensusVersion in the VersionMap. - // This will skip running InitGenesis on Foo - vm["foo"] = foo.AppModule{}.ConsensusVersion() - - return app.mm.RunMigrations(ctx, cfg, vm) + return app.mm.RunMigrations(ctx, cfg, fromVM) }) ``` diff --git a/types/module/module.go b/types/module/module.go index 0159145b4e18..148e5d860ef0 100644 --- a/types/module/module.go +++ b/types/module/module.go @@ -362,6 +362,10 @@ type VersionMap map[string]uint64 // `InitGenesis` on that module. // - return the `updatedVM` to be persisted in the x/upgrade's store. // +// Migrations are run in an alphabetical order, except x/auth which is run last. If you want +// to change the order then you should run migrations in multiple stages as described in +// docs/core/upgrade.md. +// // As an app developer, if you wish to skip running InitGenesis for your new // module "foo", you need to manually pass a `fromVM` argument to this function // foo's module version set to its latest ConsensusVersion. That way, the diff @@ -405,6 +409,8 @@ func (m Manager) RunMigrations(ctx sdk.Context, cfg Configurator, fromVM Version } } sort.Strings(sortedModNames) + // auth module must be pushed at the end because it might depend on the state from + // other modules, eg x/staking if hasAuth { sortedModNames = append(sortedModNames, authModulename) } From a5da53a54e6bc7784740a98b0f147124d3eaa0e7 Mon Sep 17 00:00:00 2001 From: Robert Zaremba Date: Thu, 25 Nov 2021 11:38:41 +0100 Subject: [PATCH 3/6] adding changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac142395ab10..a4c2acfc5630 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Bug Fixes * [\#10414](https://github.com/cosmos/cosmos-sdk/pull/10414) Use `sdk.GetConfig().GetFullBIP44Path()` instead `sdk.FullFundraiserPath` to generate key +* [\10608](https://github.com/cosmos/cosmos-sdk/pull/10608) Change the order of module migration by pushing x/auth to the end. Auth module depends on other modules and should be run fast. We have updated the documentation to provide more details how to change module migration order. This is technically a breaking change, but only impacts updates between the upgrades with version change, hence migrating from the previous patch release doesn't cause new migration and doesn't break the state. ## [v0.44.3](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.44.3) - 2021-10-21 From ad93134232ee1aa1db58313b98c855eacf0b62b8 Mon Sep 17 00:00:00 2001 From: Robert Zaremba Date: Thu, 25 Nov 2021 11:51:45 +0100 Subject: [PATCH 4/6] update docs --- docs/core/upgrade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/core/upgrade.md b/docs/core/upgrade.md index a92d675c490e..6d898e9bf466 100644 --- a/docs/core/upgrade.md +++ b/docs/core/upgrade.md @@ -125,7 +125,7 @@ func (app *MyApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.R This information is used by the Cosmos SDK to detect when modules with newer versions are introduced to the app. -For a new module `foo`, `InitGenesis` is called by the `RunMigration` only when there is a new module registered in the module manager and there is no `A` entry in the `fromVM` registered in the state. So, if you don't want to call `InitGenesis` when a new module is added to the app, then you should set zero in the `fromVM` for module `foo`: +For a new module `foo`, `InitGenesis` is called by the `RunMigration` only when there is a new module registered in the module manager and there is no `foo` entry in the `fromVM` registered in the state. Therefore, if you want to skip `InitGenesis` when a new module is added to the app, then you should set its module version in `fromVM` to the module package consensus version: ```go app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { From be215349de6972ad843e99e323083dea8a775ef3 Mon Sep 17 00:00:00 2001 From: Robert Zaremba Date: Thu, 25 Nov 2021 11:58:57 +0100 Subject: [PATCH 5/6] review updates --- docs/core/upgrade.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/core/upgrade.md b/docs/core/upgrade.md index 6d898e9bf466..4820afbbb4cc 100644 --- a/docs/core/upgrade.md +++ b/docs/core/upgrade.md @@ -65,7 +65,7 @@ To learn more about configuring migration scripts for your modules, see the [Mod ### Order Of Migrations -All migrations are run in alphabetical order, except `x/auth` which is run the last. [hack] If you want to change the order of migration then you can run migrations in multiple stages. For example, you want to run `foo` last: +All migrations are run in alphabetical order, except `x/auth` which is run the last, because of state dependencies between modules (you can read more in [issue #10606](https://github.com/cosmos/cosmos-sdk/issues/10606)). If you want to change the order of migration then you can run migrations in multiple stages. __Please beware that this is hacky, and make sure you understand what you are doing before running such migrations in production_. For example, you want to run `foo` last: ```go app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) { From 1fb49593992f57424461e6d5fe96e1e2a89c00b5 Mon Sep 17 00:00:00 2001 From: Robert Zaremba Date: Thu, 25 Nov 2021 12:03:13 +0100 Subject: [PATCH 6/6] Update CHANGELOG.md Co-authored-by: Amaury <1293565+amaurym@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4c2acfc5630..2751dd133e5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,7 +43,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Bug Fixes * [\#10414](https://github.com/cosmos/cosmos-sdk/pull/10414) Use `sdk.GetConfig().GetFullBIP44Path()` instead `sdk.FullFundraiserPath` to generate key -* [\10608](https://github.com/cosmos/cosmos-sdk/pull/10608) Change the order of module migration by pushing x/auth to the end. Auth module depends on other modules and should be run fast. We have updated the documentation to provide more details how to change module migration order. This is technically a breaking change, but only impacts updates between the upgrades with version change, hence migrating from the previous patch release doesn't cause new migration and doesn't break the state. +* [\10608](https://github.com/cosmos/cosmos-sdk/pull/10608) Change the order of module migration by pushing x/auth to the end. Auth module depends on other modules and should be run last. We have updated the documentation to provide more details how to change module migration order. This is technically a breaking change, but only impacts updates between the upgrades with version change, hence migrating from the previous patch release doesn't cause new migration and doesn't break the state. ## [v0.44.3](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.44.3) - 2021-10-21