Skip to content

Commit

Permalink
fix: always move auth module migration to the end (cosmos#10608)
Browse files Browse the repository at this point in the history
* fix: always move auth module migration to the end

* update migration docs

* adding changelog

* update docs

* review updates

* Update CHANGELOG.md

Co-authored-by: Amaury <1293565+amaurym@users.noreply.github.com>

Co-authored-by: Amaury <1293565+amaurym@users.noreply.github.com>
  • Loading branch information
2 people authored and JeancarloBarrios committed Sep 28, 2024
1 parent 435db3c commit c87ac85
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,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 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

Expand Down
29 changes: 25 additions & 4 deletions docs/learn/advanced/15-upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ Upgrade your app modules smoothly with custom in-place store migration logic. {s
- 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 Cosmos 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.
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.

### Consensus Version

Expand Down Expand Up @@ -63,6 +63,27 @@ app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx sdk.Context, plan upgrad

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, 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) {

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.
Expand Down Expand Up @@ -95,7 +116,7 @@ if upgradeInfo.Name == "my-plan" && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.
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.InitChainRequest) abci.InitChainResponse {
func (app *MyApp) InitChainer(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain {
...
+ app.UpgradeKeeper.SetModuleVersionMap(ctx, app.mm.GetVersionMap())
...
Expand All @@ -104,7 +125,7 @@ func (app *MyApp) InitChainer(ctx sdk.Context, req abci.InitChainRequest) abci.I

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 `RunMigration` only when `foo` is registered in the module manager but it's not set in the `fromVM`. 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 consensus version:
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) {
Expand Down
18 changes: 15 additions & 3 deletions types/module/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -631,8 +631,9 @@ func (m *Manager) assertNoForgottenModules(setOrderFnName string, moduleNames []
//
// - return the `updatedVM` to be persisted in the x/upgrade's store.
//
// Migrations are run in an order defined by `Manager.OrderMigrations` or (if not set) defined by
// `DefaultMigrationsOrder` function.
// 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
Expand Down Expand Up @@ -672,10 +673,21 @@ func (m Manager) RunMigrations(ctx context.Context, cfg Configurator, fromVM app
// 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)
// 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)
}

for _, moduleName := range sortedModNames {
module := m.Modules[moduleName]
Expand Down

0 comments on commit c87ac85

Please sign in to comment.