From 0985a4d728e172602a69130be7af5bad78d96830 Mon Sep 17 00:00:00 2001
From: evanorti <87997759+evanorti@users.noreply.github.com>
Date: Fri, 17 Jul 2026 16:39:07 -0400
Subject: [PATCH 1/3] cosmovisor updates
---
sdk/latest/guides/upgrades/cosmovisor.mdx | 97 +++++++++++++++--------
sdk/next/guides/upgrades/cosmovisor.mdx | 97 +++++++++++++++--------
2 files changed, 124 insertions(+), 70 deletions(-)
diff --git a/sdk/latest/guides/upgrades/cosmovisor.mdx b/sdk/latest/guides/upgrades/cosmovisor.mdx
index 0122f4fa..3512bbc7 100644
--- a/sdk/latest/guides/upgrades/cosmovisor.mdx
+++ b/sdk/latest/guides/upgrades/cosmovisor.mdx
@@ -21,6 +21,7 @@ It polls the `upgrade-info.json` file that is created by the x/upgrade module at
* [Chain Setup](#chain-setup)
* [Prepare Cosmovisor and Start the Chain](#prepare-cosmovisor-and-start-the-chain)
* [Update App](#update-app)
+* [Pre-Upgrade Handling](#pre-upgrade-handling)
## Design
@@ -31,7 +32,9 @@ Cosmovisor is designed to be used as a wrapper for a `Cosmos SDK` app:
* it will manage an app by restarting and upgrading if needed;
* it is configured using environment variables, not positional arguments.
-*Note: If new versions of the application are not set up to run in-place store migrations, migrations will need to be run manually before restarting `cosmovisor` with the new binary. For this reason, we recommend applications adopt in-place store migrations.*
+
+If new versions of the application are not set up to run in-place store migrations, migrations must be run manually before restarting `cosmovisor` with the new binary. For this reason, applications should adopt in-place store migrations.
+
Only the latest version of cosmovisor is actively developed/maintained.
@@ -51,7 +54,7 @@ Release branches have the following format `release/cosmovisor/vA.B.x`, where A
### Installation
-You can download Cosmovisor from the [GitHub releases](https://github.com/cosmos/cosmos-sdk/releases/tag/cosmovisor%2Fv1.5.0).
+You can download Cosmovisor from the [GitHub releases](https://github.com/cosmos/cosmos-sdk/releases/tag/cosmovisor%2Fv1.7.1).
To install the latest version of `cosmovisor`, run the following command:
@@ -62,7 +65,7 @@ go install cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@latest
To install a specific version, you can specify the version:
```shell
-go install cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@v1.5.0
+go install cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@v1.7.1
```
Run `cosmovisor version` to check the cosmovisor version.
@@ -85,6 +88,7 @@ The first argument passed to `cosmovisor` is the action for `cosmovisor` to take
* `add-upgrade` - Add an upgrade manually to `cosmovisor`. This command allow you to easily add the binary corresponding to an upgrade in cosmovisor.
* `add-batch-upgrade` - Add multiple upgrades at once.
* `show-upgrade-info` - Show the current upgrade info from the upgrade-info.json file.
+* `prepare-upgrade` - Download and place the next scheduled upgrade's binary ahead of time, using upgrade info read from the chain over gRPC.
All arguments passed to `cosmovisor run` will be passed to the application binary (as a subprocess). `cosmovisor` will return `/dev/stdout` and `/dev/stderr` of the subprocess as its own. For this reason, `cosmovisor run` cannot accept any command-line arguments other than those available to the application binary.
@@ -172,7 +176,7 @@ The `cosmovisor init` command is specifically for initializing cosmovisor, and s
### Detecting Upgrades
-`cosmovisor` is polling the `$DAEMON_HOME/data/upgrade-info.json` file for new upgrade instructions. The file is created by the x/upgrade module in `BeginBlocker` when an upgrade is detected and the blockchain reaches the upgrade height.
+`cosmovisor` is polling the `$DAEMON_HOME/data/upgrade-info.json` file for new upgrade instructions. The file is created by the x/upgrade module in `PreBlocker` when an upgrade is detected and the blockchain reaches the upgrade height.
The following heuristic is applied to detect the upgrade:
* When starting, `cosmovisor` doesn't know much about currently running upgrade, except the binary which is `current/bin/`. It tries to read the `current/upgrade-info.json` file to get information about the current upgrade name.
@@ -295,53 +299,55 @@ INFO Downloading upgrade binary url=https://example.com/binary/v1.0.0?checksum=s
INFO Upgrade preparation complete name=v1.0.0 height=1000000
```
-*Note: The current way of downloading manually and placing the binary at the right place would still work.*
+
+Downloading manually and placing the binary in the right location still works.
+
## Example: SimApp Upgrade
-The following instructions provide a demonstration of `cosmovisor` using the simulation application (`simapp`) shipped with the Cosmos SDK's source code. The following commands are to be run from within the `cosmos-sdk` repository.
+The following instructions demonstrate `cosmovisor` using the simulation application (`simapp`) shipped with the Cosmos SDK's source code, upgrading a chain from **v0.53.7** to **v0.54.3**. This pair uses the in-repo `v053-to-v054` upgrade handler defined in `simapp/upgrades.go` on the `v0.54.x` line, so no custom code is required. Run these commands from within a clone of the `cosmos-sdk` repository.
### Chain Setup
-Let's create a new chain using the `v0.47.4` version of simapp (the Cosmos SDK demo app):
+Build the `v0.53.7` version of `simd` (the Cosmos SDK demo app):
```shell
-git checkout v0.47.4
+git checkout v0.53.7
make build
```
-Clean `~/.simapp` (never do this in a production environment):
+Initialize the node, overwriting any previous configuration (never do this in a production environment):
```shell
-./build/simd tendermint unsafe-reset-all
+./build/simd init test --chain-id test --overwrite
```
-Set up app config:
+Set up the client config:
```shell
-./build/simd config chain-id test
-./build/simd config keyring-backend test
-./build/simd config broadcast-mode sync
+./build/simd config set client chain-id test
+./build/simd config set client keyring-backend test
+./build/simd config set client broadcast-mode sync
```
-Initialize the node and overwrite any previous genesis file (never do this in a production environment):
+Clear any previous chain data (never do this in a production environment):
```shell
-./build/simd init test --chain-id test --overwrite
+./build/simd comet unsafe-reset-all
```
-For the sake of this demonstration, amend `voting_period` in `genesis.json` to a reduced time of 20 seconds (`20s`):
+For the sake of this demonstration, reduce the governance voting period to `20s`. You must also reduce `expedited_voting_period` because it has to stay strictly less than `voting_period`, otherwise `gentx` and node startup fail genesis validation:
```shell
-cat <<< $(jq '.app_state.gov.params.voting_period = "20s"' $HOME/.simapp/config/genesis.json) > $HOME/.simapp/config/genesis.json
+cat <<< $(jq '.app_state.gov.params.voting_period = "20s" | .app_state.gov.params.expedited_voting_period = "10s"' $HOME/.simapp/config/genesis.json) > $HOME/.simapp/config/genesis.json
```
-Create a validator, and setup genesis transaction:
+Create a validator and set up the genesis transaction:
```shell
-./build/simd keys add validator
+./build/simd keys add validator --keyring-backend test
./build/simd genesis add-genesis-account validator 1000000000stake --keyring-backend test
-./build/simd genesis gentx validator 1000000stake --chain-id test
+./build/simd genesis gentx validator 1000000stake --chain-id test --keyring-backend test
./build/simd genesis collect-gentxs
```
@@ -354,19 +360,19 @@ export DAEMON_NAME=simd
export DAEMON_HOME=$HOME/.simapp
```
-Set the optional environment variable to trigger an automatic app restart:
+Set the optional environment variable to trigger an automatic app restart after the upgrade:
```shell
export DAEMON_RESTART_AFTER_UPGRADE=true
```
-Initialize cosmovisor with the current binary:
+Initialize cosmovisor with the current binary. This creates `$DAEMON_HOME/cosmovisor/genesis/bin/simd` and the `current` symlink:
```shell
cosmovisor init ./build/simd
```
-Now you can run cosmovisor with simapp v0.47.4:
+Now run the chain through cosmovisor with simapp v0.53.7:
```shell
cosmovisor run start
@@ -374,43 +380,64 @@ cosmovisor run start
### Update App
-Update app to the latest version (e.g. v0.50.0).
+Update the app to `v0.54.3`.
Migration plans are defined using the `x/upgrade` module and described in [Upgrading Modules](/sdk/latest/guides/upgrades/upgrade). Migrations can perform any deterministic state change.
-The migration plan to upgrade the simapp from v0.47 to v0.50 is defined in `simapp/upgrade.go`.
+The upgrade name and handler for this example (`const UpgradeName = "v053-to-v054"`) are defined in `simapp/upgrades.go` on the `v0.54.x` branch.
-Build the new version `simd` binary:
+In a second terminal, build the new version of `simd`:
```shell
+git checkout v0.54.3
make build
```
-Add the new `simd` binary and the upgrade name:
+Register the new binary with cosmovisor under the upgrade name. This copies it to `$DAEMON_HOME/cosmovisor/upgrades/v053-to-v054/bin/simd`:
-The migration name must match the one defined in the migration plan.
+The upgrade name must match the one defined in the migration plan (`v053-to-v054`).
```shell
-cosmovisor add-upgrade v047-to-v050 ./build/simd
+cosmovisor add-upgrade v053-to-v054 ./build/simd
```
-Open a new terminal window and submit an upgrade proposal along with a deposit and a vote (these commands must be run within 20 seconds of each other):
+Submit the software-upgrade proposal, then vote on it. Include an initial `--deposit` that meets the minimum deposit so the proposal enters the voting period immediately; both commands must run within the 20-second voting period:
```shell
-./build/simd tx upgrade software-upgrade v047-to-v050 --title upgrade --summary upgrade --upgrade-height 200 --upgrade-info "{}" --no-validate --from validator --yes
-./build/simd tx gov deposit 1 10000000stake --from validator --yes
-./build/simd tx gov vote 1 yes --from validator --yes
+./build/simd tx upgrade software-upgrade v053-to-v054 --title upgrade --summary upgrade --upgrade-height 85 --upgrade-info "{}" --no-validate --deposit 10000000stake --from validator --keyring-backend test --chain-id test --yes
+./build/simd tx gov vote 1 yes --from validator --keyring-backend test --chain-id test --yes
+```
+
+
+
+Pick an `--upgrade-height` that the chain will reach *after* the proposal passes; adjust it if your setup takes longer. With the default block time this example passes well before height 85.
+
+
+
+At the upgrade height, the running `v0.53.7` binary halts, and cosmovisor switches to the `v0.54.3` binary, runs the migrations, and restarts the node automatically:
+
+```text
+ERR UPGRADE "v053-to-v054" NEEDED at height: 85: {} module=x/upgrade
+INF pre-upgrade command does not exist. continuing the upgrade. module=cosmovisor
+INF applying upgrade "v053-to-v054" at height: 85 module=x/upgrade
+INF running migrations for module: auth module=baseapp
+...
```
-The upgrade will occur automatically at height 200. Note: you may need to change the upgrade height in the snippet above if your test play takes more time.
+Confirm the upgrade was applied and the chain has continued past the upgrade height:
+
+```shell
+./build/simd query upgrade applied v053-to-v054
+./build/simd status
+```
## Pre-Upgrade Handling
diff --git a/sdk/next/guides/upgrades/cosmovisor.mdx b/sdk/next/guides/upgrades/cosmovisor.mdx
index 3d0ed6a9..831741eb 100644
--- a/sdk/next/guides/upgrades/cosmovisor.mdx
+++ b/sdk/next/guides/upgrades/cosmovisor.mdx
@@ -22,6 +22,7 @@ It polls the `upgrade-info.json` file that is created by the x/upgrade module at
* [Chain Setup](#chain-setup)
* [Prepare Cosmovisor and Start the Chain](#prepare-cosmovisor-and-start-the-chain)
* [Update App](#update-app)
+* [Pre-Upgrade Handling](#pre-upgrade-handling)
## Design
@@ -32,7 +33,9 @@ Cosmovisor is designed to be used as a wrapper for a `Cosmos SDK` app:
* it will manage an app by restarting and upgrading if needed;
* it is configured using environment variables, not positional arguments.
-*Note: If new versions of the application are not set up to run in-place store migrations, migrations will need to be run manually before restarting `cosmovisor` with the new binary. For this reason, we recommend applications adopt in-place store migrations.*
+
+If new versions of the application are not set up to run in-place store migrations, migrations must be run manually before restarting `cosmovisor` with the new binary. For this reason, applications should adopt in-place store migrations.
+
Only the latest version of cosmovisor is actively developed/maintained.
@@ -52,7 +55,7 @@ Release branches have the following format `release/cosmovisor/vA.B.x`, where A
### Installation
-You can download Cosmovisor from the [GitHub releases](https://github.com/cosmos/cosmos-sdk/releases/tag/cosmovisor%2Fv1.5.0).
+You can download Cosmovisor from the [GitHub releases](https://github.com/cosmos/cosmos-sdk/releases/tag/cosmovisor%2Fv1.7.1).
To install the latest version of `cosmovisor`, run the following command:
@@ -63,7 +66,7 @@ go install cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@latest
To install a specific version, you can specify the version:
```shell
-go install cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@v1.5.0
+go install cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@v1.7.1
```
Run `cosmovisor version` to check the cosmovisor version.
@@ -86,6 +89,7 @@ The first argument passed to `cosmovisor` is the action for `cosmovisor` to take
* `add-upgrade` - Add an upgrade manually to `cosmovisor`. This command allow you to easily add the binary corresponding to an upgrade in cosmovisor.
* `add-batch-upgrade` - Add multiple upgrades at once.
* `show-upgrade-info` - Show the current upgrade info from the upgrade-info.json file.
+* `prepare-upgrade` - Download and place the next scheduled upgrade's binary ahead of time, using upgrade info read from the chain over gRPC.
All arguments passed to `cosmovisor run` will be passed to the application binary (as a subprocess). `cosmovisor` will return `/dev/stdout` and `/dev/stderr` of the subprocess as its own. For this reason, `cosmovisor run` cannot accept any command-line arguments other than those available to the application binary.
@@ -173,7 +177,7 @@ The `cosmovisor init` command is specifically for initializing cosmovisor, and s
### Detecting Upgrades
-`cosmovisor` is polling the `$DAEMON_HOME/data/upgrade-info.json` file for new upgrade instructions. The file is created by the x/upgrade module in `BeginBlocker` when an upgrade is detected and the blockchain reaches the upgrade height.
+`cosmovisor` is polling the `$DAEMON_HOME/data/upgrade-info.json` file for new upgrade instructions. The file is created by the x/upgrade module in `PreBlocker` when an upgrade is detected and the blockchain reaches the upgrade height.
The following heuristic is applied to detect the upgrade:
* When starting, `cosmovisor` doesn't know much about currently running upgrade, except the binary which is `current/bin/`. It tries to read the `current/upgrade-info.json` file to get information about the current upgrade name.
@@ -296,53 +300,55 @@ INFO Downloading upgrade binary url=https://example.com/binary/v1.0.0?checksum=s
INFO Upgrade preparation complete name=v1.0.0 height=1000000
```
-*Note: The current way of downloading manually and placing the binary at the right place would still work.*
+
+Downloading manually and placing the binary in the right location still works.
+
## Example: SimApp Upgrade
-The following instructions provide a demonstration of `cosmovisor` using the simulation application (`simapp`) shipped with the Cosmos SDK's source code. The following commands are to be run from within the `cosmos-sdk` repository.
+The following instructions demonstrate `cosmovisor` using the simulation application (`simapp`) shipped with the Cosmos SDK's source code, upgrading a chain from **v0.53.7** to **v0.54.3**. This pair uses the in-repo `v053-to-v054` upgrade handler defined in `simapp/upgrades.go` on the `v0.54.x` line, so no custom code is required. Run these commands from within a clone of the `cosmos-sdk` repository.
### Chain Setup
-Let's create a new chain using the `v0.47.4` version of simapp (the Cosmos SDK demo app):
+Build the `v0.53.7` version of `simd` (the Cosmos SDK demo app):
```shell
-git checkout v0.47.4
+git checkout v0.53.7
make build
```
-Clean `~/.simapp` (never do this in a production environment):
+Initialize the node, overwriting any previous configuration (never do this in a production environment):
```shell
-./build/simd tendermint unsafe-reset-all
+./build/simd init test --chain-id test --overwrite
```
-Set up app config:
+Set up the client config:
```shell
-./build/simd config chain-id test
-./build/simd config keyring-backend test
-./build/simd config broadcast-mode sync
+./build/simd config set client chain-id test
+./build/simd config set client keyring-backend test
+./build/simd config set client broadcast-mode sync
```
-Initialize the node and overwrite any previous genesis file (never do this in a production environment):
+Clear any previous chain data (never do this in a production environment):
```shell
-./build/simd init test --chain-id test --overwrite
+./build/simd comet unsafe-reset-all
```
-For the sake of this demonstration, amend `voting_period` in `genesis.json` to a reduced time of 20 seconds (`20s`):
+For the sake of this demonstration, reduce the governance voting period to `20s`. You must also reduce `expedited_voting_period` because it has to stay strictly less than `voting_period`, otherwise `gentx` and node startup fail genesis validation:
```shell
-cat <<< $(jq '.app_state.gov.params.voting_period = "20s"' $HOME/.simapp/config/genesis.json) > $HOME/.simapp/config/genesis.json
+cat <<< $(jq '.app_state.gov.params.voting_period = "20s" | .app_state.gov.params.expedited_voting_period = "10s"' $HOME/.simapp/config/genesis.json) > $HOME/.simapp/config/genesis.json
```
-Create a validator, and setup genesis transaction:
+Create a validator and set up the genesis transaction:
```shell
-./build/simd keys add validator
+./build/simd keys add validator --keyring-backend test
./build/simd genesis add-genesis-account validator 1000000000stake --keyring-backend test
-./build/simd genesis gentx validator 1000000stake --chain-id test
+./build/simd genesis gentx validator 1000000stake --chain-id test --keyring-backend test
./build/simd genesis collect-gentxs
```
@@ -355,19 +361,19 @@ export DAEMON_NAME=simd
export DAEMON_HOME=$HOME/.simapp
```
-Set the optional environment variable to trigger an automatic app restart:
+Set the optional environment variable to trigger an automatic app restart after the upgrade:
```shell
export DAEMON_RESTART_AFTER_UPGRADE=true
```
-Initialize cosmovisor with the current binary:
+Initialize cosmovisor with the current binary. This creates `$DAEMON_HOME/cosmovisor/genesis/bin/simd` and the `current` symlink:
```shell
cosmovisor init ./build/simd
```
-Now you can run cosmovisor with simapp v0.47.4:
+Now run the chain through cosmovisor with simapp v0.53.7:
```shell
cosmovisor run start
@@ -375,43 +381,64 @@ cosmovisor run start
### Update App
-Update app to the latest version (e.g. v0.50.0).
+Update the app to `v0.54.3`.
Migration plans are defined using the `x/upgrade` module and described in [Upgrading Modules](/sdk/next/guides/upgrades/upgrade). Migrations can perform any deterministic state change.
-The migration plan to upgrade the simapp from v0.47 to v0.50 is defined in `simapp/upgrade.go`.
+The upgrade name and handler for this example (`const UpgradeName = "v053-to-v054"`) are defined in `simapp/upgrades.go` on the `v0.54.x` branch.
-Build the new version `simd` binary:
+In a second terminal, build the new version of `simd`:
```shell
+git checkout v0.54.3
make build
```
-Add the new `simd` binary and the upgrade name:
+Register the new binary with cosmovisor under the upgrade name. This copies it to `$DAEMON_HOME/cosmovisor/upgrades/v053-to-v054/bin/simd`:
-The migration name must match the one defined in the migration plan.
+The upgrade name must match the one defined in the migration plan (`v053-to-v054`).
```shell
-cosmovisor add-upgrade v047-to-v050 ./build/simd
+cosmovisor add-upgrade v053-to-v054 ./build/simd
```
-Open a new terminal window and submit an upgrade proposal along with a deposit and a vote (these commands must be run within 20 seconds of each other):
+Submit the software-upgrade proposal, then vote on it. Include an initial `--deposit` that meets the minimum deposit so the proposal enters the voting period immediately; both commands must run within the 20-second voting period:
```shell
-./build/simd tx upgrade software-upgrade v047-to-v050 --title upgrade --summary upgrade --upgrade-height 200 --upgrade-info "{}" --no-validate --from validator --yes
-./build/simd tx gov deposit 1 10000000stake --from validator --yes
-./build/simd tx gov vote 1 yes --from validator --yes
+./build/simd tx upgrade software-upgrade v053-to-v054 --title upgrade --summary upgrade --upgrade-height 85 --upgrade-info "{}" --no-validate --deposit 10000000stake --from validator --keyring-backend test --chain-id test --yes
+./build/simd tx gov vote 1 yes --from validator --keyring-backend test --chain-id test --yes
+```
+
+
+
+Pick an `--upgrade-height` that the chain will reach *after* the proposal passes; adjust it if your setup takes longer. With the default block time this example passes well before height 85.
+
+
+
+At the upgrade height, the running `v0.53.7` binary halts, and cosmovisor switches to the `v0.54.3` binary, runs the migrations, and restarts the node automatically:
+
+```text
+ERR UPGRADE "v053-to-v054" NEEDED at height: 85: {} module=x/upgrade
+INF pre-upgrade command does not exist. continuing the upgrade. module=cosmovisor
+INF applying upgrade "v053-to-v054" at height: 85 module=x/upgrade
+INF running migrations for module: auth module=baseapp
+...
```
-The upgrade will occur automatically at height 200. Note: you may need to change the upgrade height in the snippet above if your test play takes more time.
+Confirm the upgrade was applied and the chain has continued past the upgrade height:
+
+```shell
+./build/simd query upgrade applied v053-to-v054
+./build/simd status
+```
## Pre-Upgrade Handling
From de57e36df1cbbe91d23c7922364e3d9d85d807d6 Mon Sep 17 00:00:00 2001
From: evanorti <87997759+evanorti@users.noreply.github.com>
Date: Fri, 17 Jul 2026 16:39:19 -0400
Subject: [PATCH 2/3] update update guides
---
sdk/latest/guides/upgrades/upgrade.mdx | 198 ++++++++++++++++++++-----
sdk/next/guides/upgrades/upgrade.mdx | 198 ++++++++++++++++++++-----
2 files changed, 316 insertions(+), 80 deletions(-)
diff --git a/sdk/latest/guides/upgrades/upgrade.mdx b/sdk/latest/guides/upgrades/upgrade.mdx
index eccb1b5c..99183df3 100644
--- a/sdk/latest/guides/upgrades/upgrade.mdx
+++ b/sdk/latest/guides/upgrades/upgrade.mdx
@@ -8,29 +8,102 @@ Read and understand all of this page before running a migration on a live chain.
**Synopsis**
-In-place store migrations allow modules to upgrade to new versions that include breaking changes. This document covers both the module-side (writing migrations) and the app-side (running migrations during an upgrade).
+In-place store migrations let modules ship breaking state changes during a chain upgrade. This page covers how an upgrade runs, setting up `x/upgrade`, writing and registering migrations, running them in an upgrade handler, and a worked example.
-The Cosmos SDK supports two approaches to chain upgrades: exporting the entire application state to JSON and starting fresh with a modified genesis file, or performing in-place store migrations that update state directly. In-place migrations are significantly faster for chains with large state and are the standard approach for live networks.
+The Cosmos SDK supports two approaches to chain upgrades: exporting the entire application state to JSON and starting fresh with a modified genesis file, or performing in-place store migrations that update state directly. In-place migrations are significantly faster for chains with large state and are the standard approach for live networks. This page covers the in-place approach.
-This page covers how to write module migrations and how to run them inside an upgrade handler in your app.
+## How an upgrade works
-## Consensus Version
+An upgrade is scheduled on-chain, usually through governance, and runs in this order:
+
+1. Someone submits a governance proposal containing a `MsgSoftwareUpgrade` whose `Plan` names the upgrade (matching the name passed to `SetUpgradeHandler`) and sets a target height.
+2. Validators vote. If the proposal passes, `x/upgrade` records the plan.
+3. At the plan height every node halts and writes `upgrade-info.json` to its home directory.
+4. The node operator, or Cosmovisor, starts the new binary. During the next block's `PreBlock`, `x/upgrade` sees the due plan and runs the registered upgrade handler, which calls `RunMigrations`.
+5. The chain continues on the new binary with migrated state.
+
+A `Plan` is the on-chain upgrade record: a name and a target height. The `VersionMap` is a map of module name to consensus version, stored by `x/upgrade`, recording the version each module's state was last migrated to. The sections below cover each piece. The [Cosmovisor](/sdk/latest/guides/upgrades/cosmovisor) guide covers the node-operator side.
+
+## Consensus version
Successful upgrades of existing modules require each `AppModule` to implement the function `ConsensusVersion() uint64`.
* The versions must be hard-coded by the module developer.
* The initial version **must** be set to 1.
-Consensus versions serve as state-breaking versions of app modules and must be incremented when the module introduces breaking changes.
+Consensus versions serve as state-breaking versions of app modules and must be incremented when the module introduces breaking changes. `RunMigrations` compares these against the `VersionMap` to decide which migrations to run.
+
+## Set up x/upgrade
+
+The rest of this page assumes the app has `x/upgrade` wired in. Create the `UpgradeKeeper` before the module manager so the upgrade module can be registered, register the module, and run its `PreBlocker`. See the full wiring in the [cosmos/example](https://github.com/cosmos/example) app.
+
+```go expandable
+import (
+ "github.com/cosmos/cosmos-sdk/x/upgrade"
+ upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper"
+ upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
+)
+
+// Reserve a store key for x/upgrade alongside the other module keys:
+// upgradetypes.StoreKey
+
+// Create the UpgradeKeeper before the module manager. homePath is where
+// ReadUpgradeInfoFromDisk looks for the upgrade-info.json a node writes at the
+// halt height. The authority is usually the governance module account.
+app.UpgradeKeeper = upgradekeeper.NewKeeper(
+ skipUpgradeHeights,
+ runtime.NewKVStoreService(keys[upgradetypes.StoreKey]),
+ appCodec,
+ homePath,
+ app.BaseApp,
+ authtypes.NewModuleAddress(govtypes.ModuleName).String(),
+)
+
+// Register the upgrade module with the module manager.
+app.ModuleManager = module.NewManager(
+ // other modules...
+ upgrade.NewAppModule(app.UpgradeKeeper, app.AccountKeeper.AddressCodec()),
+)
+
+// Run x/upgrade first in PreBlock so it can detect a due plan and execute the
+// handler before the rest of the block.
+app.ModuleManager.SetOrderPreBlockers(
+ upgradetypes.ModuleName,
+ // other pre-blockers...
+)
+app.SetPreBlocker(app.PreBlocker)
+```
+
+The `PreBlocker` method runs the module manager's `PreBlock`, which is where `x/upgrade` detects a due plan and executes its handler:
+
+```go
+func (app *MyApp) PreBlocker(ctx sdk.Context, _ *abci.RequestFinalizeBlock) (*sdk.ResponsePreBlock, error) {
+ return app.ModuleManager.PreBlock(ctx)
+}
+```
+
+
+`x/upgrade` only runs during `PreBlock`. If `SetPreBlocker` is not wired to a `PreBlocker` that calls the module manager's `PreBlock`, upgrade plans never execute and no migrations run.
+
+
+Also save the consensus version of each module to state at genesis, so future upgrades can detect when modules with newer consensus versions are introduced. Add this to `InitChainer`:
-## Registering Migrations
+```go
+func (app *MyApp) InitChainer(ctx sdk.Context, req *abci.RequestInitChain) (*abci.ResponseInitChain, error) {
+ // ...
+ if err := app.UpgradeKeeper.SetModuleVersionMap(ctx, app.ModuleManager.GetVersionMap()); err != nil {
+ return nil, err
+ }
+ // ...
+}
+```
-To register the functionality that takes place during a module upgrade, you must register which migrations you want to take place.
+## Registering migrations
-Migration registration takes place in the `Configurator` using the `RegisterMigration` method. The `AppModule` reference to the configurator is in the `RegisterServices` method.
+To register the functionality that takes place during a module upgrade, register the migrations in the `Configurator` using its `RegisterMigration` method, from the `AppModule`'s `RegisterServices` method.
-You can register one or more migrations. If you register more than one migration script, list the migrations in increasing order and ensure there are enough migrations that lead to the desired consensus version. For example, to migrate to version 3 of a module, register separate migrations for version 1 and version 2 as shown in the following example:
+Register migrations in increasing order, one per source version, up to the target consensus version. For example, to migrate to version 3 of a module, register migrations for versions 1 and 2:
```go
func (am AppModule) RegisterServices(cfg module.Configurator) {
@@ -51,7 +124,40 @@ func (am AppModule) RegisterServices(cfg module.Configurator) {
}
```
-Since these migrations are functions that need access to a Keeper's store, use a wrapper around the keepers called `Migrator` as shown in this example:
+The migration functions need access to the keeper's store, so they are defined as methods on a `Migrator` that wraps the keeper. The next section writes one.
+
+## Writing migration scripts
+
+A migration reads the module's existing state and rewrites it into the new layout. Place migration functions in the module's `keeper` package, or in a versioned `migrations/` directory for larger modules (for example `x/bank/migrations/v2`).
+
+The following `Migrator` moves the counter module from consensus version 1 to 2. The breaking change is a re-denomination: every stored count is multiplied by 10. `Migrate1to2` reads the current value, transforms it, and writes it back, treating an unset value as a no-op rather than an error:
+
+```go
+type Migrator struct {
+ keeper *Keeper
+}
+
+func NewMigrator(keeper *Keeper) Migrator {
+ return Migrator{keeper: keeper}
+}
+
+func (m Migrator) Migrate1to2(ctx sdk.Context) error {
+ count, err := m.keeper.counter.Get(ctx)
+ if err != nil {
+ // A chain that never touched the counter has no stored value yet.
+ if errors.Is(err, collections.ErrNotFound) {
+ return nil
+ }
+ return err
+ }
+
+ return m.keeper.counter.Set(ctx, count*10)
+}
+```
+
+Register this migration with `RegisterMigration(types.ModuleName, 1, m.Migrate1to2)` as shown in the previous section.
+
+For larger modules, keep the transformation in a versioned package so the `Migrator` method stays a thin wrapper. The bank module follows this pattern:
```go expandable
package keeper
@@ -92,20 +198,9 @@ func (m Migrator) Migrate3to4(ctx sdk.Context) error {
}
```
-## Writing Migration Scripts
-
-To define the functionality that takes place during an upgrade, write a migration script and place the functions in a `migrations/` directory. For example, to write migration scripts for the bank module, place the functions in `x/bank/migrations/`. Import each version package and call its `MigrateStore` function from the corresponding `Migrator` method:
-
-```go
-// Migrating bank module from version 1 to 2
-func (m Migrator) Migrate1to2(ctx sdk.Context) error {
- return v2.MigrateStore(ctx, m.keeper.storeService, m.keeper.cdc) // v2 is package `x/bank/migrations/v2`.
-}
-```
-
-To see example code of changes that were implemented in a migration of balance keys, check out [migrateBalanceKeys](https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/x/bank/migrations/v2/store.go#L55-L76). For context, this code introduced migrations of the bank store that updated addresses to be prefixed by their length in bytes as outlined in [ADR-028](/sdk/latest/reference/architecture/adr-028-public-key-addresses).
+For a production example that manipulates raw KV store keys, see [migrateBalanceKeys](https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/x/bank/migrations/v2/store.go#L55-L76). This code updated bank addresses to be prefixed by their length in bytes as outlined in [ADR-028](/sdk/latest/reference/architecture/adr-028-public-key-addresses).
-## Running Migrations in the App
+## Running migrations in the app
Once modules have registered their migrations, the app runs them inside an `UpgradeHandler`. The upgrade handler type is:
@@ -113,6 +208,10 @@ Once modules have registered their migrations, the app runs them inside an `Upgr
type UpgradeHandler func(ctx context.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error)
```
+
+ As of Cosmos SDK v0.54, `x/upgrade` is part of the main SDK module. Import it from `github.com/cosmos/cosmos-sdk/x/upgrade`, not the standalone `cosmossdk.io/x/upgrade` module, which is not compatible with v0.54.
+
+
The handler receives the `VersionMap` stored by `x/upgrade` (reflecting the consensus versions from the previous binary), performs any additional upgrade logic, and must return the updated `VersionMap` from `RunMigrations`. Register the handler in `app.go`:
```go
@@ -132,7 +231,7 @@ By default, migrations run in alphabetical order by module name, with one except
New modules are recognized because they have no entry in the `x/upgrade` `VersionMap` store. `RunMigrations` calls `InitGenesis` for them automatically.
-If you need to add stores for a new module, configure the store loader before the upgrade runs:
+If you need to add stores for a new module, configure the store loader before the upgrade runs. The loader reads `upgrade-info.json` at startup and adds the store at the upgrade height:
```go
upgradeInfo, err := app.UpgradeKeeper.ReadUpgradeInfoFromDisk()
@@ -147,6 +246,8 @@ if upgradeInfo.Name == "my-plan" && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.
}
```
+Configure the store loader in the app constructor, before `LoadLatestVersion` runs, so the new store is mounted when the store loads.
+
To skip `InitGenesis` for a new module (for example, if you are manually initializing state in the handler), set its version in `fromVM` before calling `RunMigrations`:
```go
@@ -154,20 +255,6 @@ fromVM["newmodule"] = newmodule.AppModule{}.ConsensusVersion()
return app.ModuleManager.RunMigrations(ctx, app.Configurator(), fromVM)
```
-### Genesis state
-
-When starting a new chain, the consensus version of each module must be saved to state during genesis. Add this to `InitChainer` in `app.go`:
-
-```go
-func (app *MyApp) InitChainer(ctx sdk.Context, req *abci.RequestInitChain) (*abci.ResponseInitChain, error) {
- // ...
- app.UpgradeKeeper.SetModuleVersionMap(ctx, app.ModuleManager.GetVersionMap())
- // ...
-}
-```
-
-This lets the Cosmos SDK detect when modules with newer consensus versions are introduced in a future upgrade.
-
### Overwriting genesis functions
The SDK provides modules that app developers can import, and those modules often already have an `InitGenesis` function. If you want to run a custom genesis function for one of those modules during an upgrade instead of the default one, you must both call your custom function in the handler AND manually set that module's consensus version in `fromVM`. Without the second step, `RunMigrations` will run the module's existing `InitGenesis` even though you already initialized it.
@@ -184,13 +271,44 @@ app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx context.Context, plan up
fromVM["foo"] = foo.AppModule{}.ConsensusVersion()
// Run your custom genesis initialization for foo.
- app.ModuleManager.Modules["foo"].(module.HasGenesis).InitGenesis(ctx, app.appCodec, myCustomGenesisState)
+ // InitGenesis takes sdk.Context, so unwrap the handler's context.Context.
+ // myCustomGenesisState must be a json.RawMessage (the marshaled genesis state).
+ app.ModuleManager.Modules["foo"].(module.HasGenesis).InitGenesis(sdk.UnwrapSDKContext(ctx), app.AppCodec(), myCustomGenesisState)
return app.ModuleManager.RunMigrations(ctx, app.Configurator(), fromVM)
})
```
-## Syncing a Full Node to an Upgraded Blockchain
+## Counter module upgrade example
+
+The counter module is built step by step in the [example chain tutorial](/sdk/latest/tutorials/example/00-overview), and its source lives in the [cosmos/example](https://github.com/cosmos/example) repository. This example continues from that module: it bumps the counter to consensus version 2 and runs the migration shown earlier during an upgrade named `my-plan`.
+
+1. Increment the module's `ConsensusVersion` so the SDK detects that its state layout changed:
+
+ ```go
+ func (AppModule) ConsensusVersion() uint64 { return 2 }
+ ```
+
+2. In `RegisterServices`, wrap the keeper in a `Migrator` and register the version 1 to 2 migration:
+
+ ```go
+ m := keeper.NewMigrator(am.keeper)
+ if err := cfg.RegisterMigration(types.ModuleName, 1, m.Migrate1to2); err != nil {
+ panic(fmt.Sprintf("failed to migrate x/%s from version 1 to 2: %v", types.ModuleName, err))
+ }
+ ```
+
+3. In `app.go`, register a handler for the plan that calls `RunMigrations`:
+
+ ```go
+ app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx context.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) {
+ return app.ModuleManager.RunMigrations(ctx, app.Configurator(), fromVM)
+ })
+ ```
+
+4. Schedule the `my-plan` upgrade through governance. When the chain reaches the plan height it halts, the node operator starts the new binary, and the handler runs the counter migration during `PreBlock`. See [How an upgrade works](#how-an-upgrade-works).
+
+## Syncing a full node to an upgraded blockchain
A full node joining an already-upgraded chain must start from the initial binary that the chain used at genesis and replay all historical upgrades. If all upgrade plans include binary download instructions, Cosmovisor's auto-download mode handles this automatically. Otherwise, you must provide each historical binary manually.
diff --git a/sdk/next/guides/upgrades/upgrade.mdx b/sdk/next/guides/upgrades/upgrade.mdx
index f893d180..3bb2d7c7 100644
--- a/sdk/next/guides/upgrades/upgrade.mdx
+++ b/sdk/next/guides/upgrades/upgrade.mdx
@@ -9,29 +9,102 @@ Read and understand all of this page before running a migration on a live chain.
**Synopsis**
-In-place store migrations allow modules to upgrade to new versions that include breaking changes. This document covers both the module-side (writing migrations) and the app-side (running migrations during an upgrade).
+In-place store migrations let modules ship breaking state changes during a chain upgrade. This page covers how an upgrade runs, setting up `x/upgrade`, writing and registering migrations, running them in an upgrade handler, and a worked example.
-The Cosmos SDK supports two approaches to chain upgrades: exporting the entire application state to JSON and starting fresh with a modified genesis file, or performing in-place store migrations that update state directly. In-place migrations are significantly faster for chains with large state and are the standard approach for live networks.
+The Cosmos SDK supports two approaches to chain upgrades: exporting the entire application state to JSON and starting fresh with a modified genesis file, or performing in-place store migrations that update state directly. In-place migrations are significantly faster for chains with large state and are the standard approach for live networks. This page covers the in-place approach.
-This page covers how to write module migrations and how to run them inside an upgrade handler in your app.
+## How an upgrade works
-## Consensus Version
+An upgrade is scheduled on-chain, usually through governance, and runs in this order:
+
+1. Someone submits a governance proposal containing a `MsgSoftwareUpgrade` whose `Plan` names the upgrade (matching the name passed to `SetUpgradeHandler`) and sets a target height.
+2. Validators vote. If the proposal passes, `x/upgrade` records the plan.
+3. At the plan height every node halts and writes `upgrade-info.json` to its home directory.
+4. The node operator, or Cosmovisor, starts the new binary. During the next block's `PreBlock`, `x/upgrade` sees the due plan and runs the registered upgrade handler, which calls `RunMigrations`.
+5. The chain continues on the new binary with migrated state.
+
+A `Plan` is the on-chain upgrade record: a name and a target height. The `VersionMap` is a map of module name to consensus version, stored by `x/upgrade`, recording the version each module's state was last migrated to. The sections below cover each piece. The [Cosmovisor](/sdk/next/guides/upgrades/cosmovisor) guide covers the node-operator side.
+
+## Consensus version
Successful upgrades of existing modules require each `AppModule` to implement the function `ConsensusVersion() uint64`.
* The versions must be hard-coded by the module developer.
* The initial version **must** be set to 1.
-Consensus versions serve as state-breaking versions of app modules and must be incremented when the module introduces breaking changes.
+Consensus versions serve as state-breaking versions of app modules and must be incremented when the module introduces breaking changes. `RunMigrations` compares these against the `VersionMap` to decide which migrations to run.
+
+## Set up x/upgrade
+
+The rest of this page assumes the app has `x/upgrade` wired in. Create the `UpgradeKeeper` before the module manager so the upgrade module can be registered, register the module, and run its `PreBlocker`. See the full wiring in the [cosmos/example](https://github.com/cosmos/example) app.
+
+```go expandable
+import (
+ "github.com/cosmos/cosmos-sdk/x/upgrade"
+ upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper"
+ upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
+)
+
+// Reserve a store key for x/upgrade alongside the other module keys:
+// upgradetypes.StoreKey
+
+// Create the UpgradeKeeper before the module manager. homePath is where
+// ReadUpgradeInfoFromDisk looks for the upgrade-info.json a node writes at the
+// halt height. The authority is usually the governance module account.
+app.UpgradeKeeper = upgradekeeper.NewKeeper(
+ skipUpgradeHeights,
+ runtime.NewKVStoreService(keys[upgradetypes.StoreKey]),
+ appCodec,
+ homePath,
+ app.BaseApp,
+ authtypes.NewModuleAddress(govtypes.ModuleName).String(),
+)
+
+// Register the upgrade module with the module manager.
+app.ModuleManager = module.NewManager(
+ // other modules...
+ upgrade.NewAppModule(app.UpgradeKeeper, app.AccountKeeper.AddressCodec()),
+)
+
+// Run x/upgrade first in PreBlock so it can detect a due plan and execute the
+// handler before the rest of the block.
+app.ModuleManager.SetOrderPreBlockers(
+ upgradetypes.ModuleName,
+ // other pre-blockers...
+)
+app.SetPreBlocker(app.PreBlocker)
+```
+
+The `PreBlocker` method runs the module manager's `PreBlock`, which is where `x/upgrade` detects a due plan and executes its handler:
+
+```go
+func (app *MyApp) PreBlocker(ctx sdk.Context, _ *abci.RequestFinalizeBlock) (*sdk.ResponsePreBlock, error) {
+ return app.ModuleManager.PreBlock(ctx)
+}
+```
+
+
+`x/upgrade` only runs during `PreBlock`. If `SetPreBlocker` is not wired to a `PreBlocker` that calls the module manager's `PreBlock`, upgrade plans never execute and no migrations run.
+
+
+Also save the consensus version of each module to state at genesis, so future upgrades can detect when modules with newer consensus versions are introduced. Add this to `InitChainer`:
-## Registering Migrations
+```go
+func (app *MyApp) InitChainer(ctx sdk.Context, req *abci.RequestInitChain) (*abci.ResponseInitChain, error) {
+ // ...
+ if err := app.UpgradeKeeper.SetModuleVersionMap(ctx, app.ModuleManager.GetVersionMap()); err != nil {
+ return nil, err
+ }
+ // ...
+}
+```
-To register the functionality that takes place during a module upgrade, you must register which migrations you want to take place.
+## Registering migrations
-Migration registration takes place in the `Configurator` using the `RegisterMigration` method. The `AppModule` reference to the configurator is in the `RegisterServices` method.
+To register the functionality that takes place during a module upgrade, register the migrations in the `Configurator` using its `RegisterMigration` method, from the `AppModule`'s `RegisterServices` method.
-You can register one or more migrations. If you register more than one migration script, list the migrations in increasing order and ensure there are enough migrations that lead to the desired consensus version. For example, to migrate to version 3 of a module, register separate migrations for version 1 and version 2 as shown in the following example:
+Register migrations in increasing order, one per source version, up to the target consensus version. For example, to migrate to version 3 of a module, register migrations for versions 1 and 2:
```go
func (am AppModule) RegisterServices(cfg module.Configurator) {
@@ -52,7 +125,40 @@ func (am AppModule) RegisterServices(cfg module.Configurator) {
}
```
-Since these migrations are functions that need access to a Keeper's store, use a wrapper around the keepers called `Migrator` as shown in this example:
+The migration functions need access to the keeper's store, so they are defined as methods on a `Migrator` that wraps the keeper. The next section writes one.
+
+## Writing migration scripts
+
+A migration reads the module's existing state and rewrites it into the new layout. Place migration functions in the module's `keeper` package, or in a versioned `migrations/` directory for larger modules (for example `x/bank/migrations/v2`).
+
+The following `Migrator` moves the counter module from consensus version 1 to 2. The breaking change is a re-denomination: every stored count is multiplied by 10. `Migrate1to2` reads the current value, transforms it, and writes it back, treating an unset value as a no-op rather than an error:
+
+```go
+type Migrator struct {
+ keeper *Keeper
+}
+
+func NewMigrator(keeper *Keeper) Migrator {
+ return Migrator{keeper: keeper}
+}
+
+func (m Migrator) Migrate1to2(ctx sdk.Context) error {
+ count, err := m.keeper.counter.Get(ctx)
+ if err != nil {
+ // A chain that never touched the counter has no stored value yet.
+ if errors.Is(err, collections.ErrNotFound) {
+ return nil
+ }
+ return err
+ }
+
+ return m.keeper.counter.Set(ctx, count*10)
+}
+```
+
+Register this migration with `RegisterMigration(types.ModuleName, 1, m.Migrate1to2)` as shown in the previous section.
+
+For larger modules, keep the transformation in a versioned package so the `Migrator` method stays a thin wrapper. The bank module follows this pattern:
```go expandable
package keeper
@@ -93,20 +199,9 @@ func (m Migrator) Migrate3to4(ctx sdk.Context) error {
}
```
-## Writing Migration Scripts
-
-To define the functionality that takes place during an upgrade, write a migration script and place the functions in a `migrations/` directory. For example, to write migration scripts for the bank module, place the functions in `x/bank/migrations/`. Import each version package and call its `MigrateStore` function from the corresponding `Migrator` method:
-
-```go
-// Migrating bank module from version 1 to 2
-func (m Migrator) Migrate1to2(ctx sdk.Context) error {
- return v2.MigrateStore(ctx, m.keeper.storeService, m.keeper.cdc) // v2 is package `x/bank/migrations/v2`.
-}
-```
-
-To see example code of changes that were implemented in a migration of balance keys, check out [migrateBalanceKeys](https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/x/bank/migrations/v2/store.go#L55-L76). For context, this code introduced migrations of the bank store that updated addresses to be prefixed by their length in bytes as outlined in [ADR-028](/sdk/next/reference/architecture/adr-028-public-key-addresses).
+For a production example that manipulates raw KV store keys, see [migrateBalanceKeys](https://github.com/cosmos/cosmos-sdk/blob/release/v0.54.x/x/bank/migrations/v2/store.go#L55-L76). This code updated bank addresses to be prefixed by their length in bytes as outlined in [ADR-028](/sdk/next/reference/architecture/adr-028-public-key-addresses).
-## Running Migrations in the App
+## Running migrations in the app
Once modules have registered their migrations, the app runs them inside an `UpgradeHandler`. The upgrade handler type is:
@@ -114,6 +209,10 @@ Once modules have registered their migrations, the app runs them inside an `Upgr
type UpgradeHandler func(ctx context.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error)
```
+
+ As of Cosmos SDK v0.54, `x/upgrade` is part of the main SDK module. Import it from `github.com/cosmos/cosmos-sdk/x/upgrade`, not the standalone `cosmossdk.io/x/upgrade` module, which is not compatible with v0.54.
+
+
The handler receives the `VersionMap` stored by `x/upgrade` (reflecting the consensus versions from the previous binary), performs any additional upgrade logic, and must return the updated `VersionMap` from `RunMigrations`. Register the handler in `app.go`:
```go
@@ -133,7 +232,7 @@ By default, migrations run in alphabetical order by module name, with one except
New modules are recognized because they have no entry in the `x/upgrade` `VersionMap` store. `RunMigrations` calls `InitGenesis` for them automatically.
-If you need to add stores for a new module, configure the store loader before the upgrade runs:
+If you need to add stores for a new module, configure the store loader before the upgrade runs. The loader reads `upgrade-info.json` at startup and adds the store at the upgrade height:
```go
upgradeInfo, err := app.UpgradeKeeper.ReadUpgradeInfoFromDisk()
@@ -148,6 +247,8 @@ if upgradeInfo.Name == "my-plan" && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.
}
```
+Configure the store loader in the app constructor, before `LoadLatestVersion` runs, so the new store is mounted when the store loads.
+
To skip `InitGenesis` for a new module (for example, if you are manually initializing state in the handler), set its version in `fromVM` before calling `RunMigrations`:
```go
@@ -155,20 +256,6 @@ fromVM["newmodule"] = newmodule.AppModule{}.ConsensusVersion()
return app.ModuleManager.RunMigrations(ctx, app.Configurator(), fromVM)
```
-### Genesis state
-
-When starting a new chain, the consensus version of each module must be saved to state during genesis. Add this to `InitChainer` in `app.go`:
-
-```go
-func (app *MyApp) InitChainer(ctx sdk.Context, req *abci.RequestInitChain) (*abci.ResponseInitChain, error) {
- // ...
- app.UpgradeKeeper.SetModuleVersionMap(ctx, app.ModuleManager.GetVersionMap())
- // ...
-}
-```
-
-This lets the Cosmos SDK detect when modules with newer consensus versions are introduced in a future upgrade.
-
### Overwriting genesis functions
The SDK provides modules that app developers can import, and those modules often already have an `InitGenesis` function. If you want to run a custom genesis function for one of those modules during an upgrade instead of the default one, you must both call your custom function in the handler AND manually set that module's consensus version in `fromVM`. Without the second step, `RunMigrations` will run the module's existing `InitGenesis` even though you already initialized it.
@@ -185,13 +272,44 @@ app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx context.Context, plan up
fromVM["foo"] = foo.AppModule{}.ConsensusVersion()
// Run your custom genesis initialization for foo.
- app.ModuleManager.Modules["foo"].(module.HasGenesis).InitGenesis(ctx, app.appCodec, myCustomGenesisState)
+ // InitGenesis takes sdk.Context, so unwrap the handler's context.Context.
+ // myCustomGenesisState must be a json.RawMessage (the marshaled genesis state).
+ app.ModuleManager.Modules["foo"].(module.HasGenesis).InitGenesis(sdk.UnwrapSDKContext(ctx), app.AppCodec(), myCustomGenesisState)
return app.ModuleManager.RunMigrations(ctx, app.Configurator(), fromVM)
})
```
-## Syncing a Full Node to an Upgraded Blockchain
+## Counter module upgrade example
+
+The counter module is built step by step in the [example chain tutorial](/sdk/next/tutorials/example/00-overview), and its source lives in the [cosmos/example](https://github.com/cosmos/example) repository. This example continues from that module: it bumps the counter to consensus version 2 and runs the migration shown earlier during an upgrade named `my-plan`.
+
+1. Increment the module's `ConsensusVersion` so the SDK detects that its state layout changed:
+
+ ```go
+ func (AppModule) ConsensusVersion() uint64 { return 2 }
+ ```
+
+2. In `RegisterServices`, wrap the keeper in a `Migrator` and register the version 1 to 2 migration:
+
+ ```go
+ m := keeper.NewMigrator(am.keeper)
+ if err := cfg.RegisterMigration(types.ModuleName, 1, m.Migrate1to2); err != nil {
+ panic(fmt.Sprintf("failed to migrate x/%s from version 1 to 2: %v", types.ModuleName, err))
+ }
+ ```
+
+3. In `app.go`, register a handler for the plan that calls `RunMigrations`:
+
+ ```go
+ app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx context.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) {
+ return app.ModuleManager.RunMigrations(ctx, app.Configurator(), fromVM)
+ })
+ ```
+
+4. Schedule the `my-plan` upgrade through governance. When the chain reaches the plan height it halts, the node operator starts the new binary, and the handler runs the counter migration during `PreBlock`. See [How an upgrade works](#how-an-upgrade-works).
+
+## Syncing a full node to an upgraded blockchain
A full node joining an already-upgraded chain must start from the initial binary that the chain used at genesis and replay all historical upgrades. If all upgrade plans include binary download instructions, Cosmovisor's auto-download mode handles this automatically. Otherwise, you must provide each historical binary manually.
From a566ab58f709b92bd05e63570ead8db57216b02f Mon Sep 17 00:00:00 2001
From: Evan <87997759+evanorti@users.noreply.github.com>
Date: Fri, 17 Jul 2026 16:54:32 -0400
Subject: [PATCH 3/3] Apply suggestions from code review
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
---
sdk/latest/guides/upgrades/cosmovisor.mdx | 2 +-
sdk/next/guides/upgrades/cosmovisor.mdx | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/sdk/latest/guides/upgrades/cosmovisor.mdx b/sdk/latest/guides/upgrades/cosmovisor.mdx
index 3512bbc7..b8ec3054 100644
--- a/sdk/latest/guides/upgrades/cosmovisor.mdx
+++ b/sdk/latest/guides/upgrades/cosmovisor.mdx
@@ -305,7 +305,7 @@ Downloading manually and placing the binary in the right location still works.
## Example: SimApp Upgrade
-The following instructions demonstrate `cosmovisor` using the simulation application (`simapp`) shipped with the Cosmos SDK's source code, upgrading a chain from **v0.53.7** to **v0.54.3**. This pair uses the in-repo `v053-to-v054` upgrade handler defined in `simapp/upgrades.go` on the `v0.54.x` line, so no custom code is required. Run these commands from within a clone of the `cosmos-sdk` repository.
+The following instructions demonstrate `cosmovisor` using the simulation application (`simapp`) shipped with the Cosmos SDK's source code, upgrading a chain from `v0.53.7` to `v0.54.3`. This pair uses the in-repo `v053-to-v054` upgrade handler defined in `simapp/upgrades.go` on the `v0.54.x` line, so no custom code is required. Run these commands from within a clone of the `cosmos-sdk` repository.
### Chain Setup
diff --git a/sdk/next/guides/upgrades/cosmovisor.mdx b/sdk/next/guides/upgrades/cosmovisor.mdx
index 831741eb..e4c7e118 100644
--- a/sdk/next/guides/upgrades/cosmovisor.mdx
+++ b/sdk/next/guides/upgrades/cosmovisor.mdx
@@ -306,7 +306,7 @@ Downloading manually and placing the binary in the right location still works.
## Example: SimApp Upgrade
-The following instructions demonstrate `cosmovisor` using the simulation application (`simapp`) shipped with the Cosmos SDK's source code, upgrading a chain from **v0.53.7** to **v0.54.3**. This pair uses the in-repo `v053-to-v054` upgrade handler defined in `simapp/upgrades.go` on the `v0.54.x` line, so no custom code is required. Run these commands from within a clone of the `cosmos-sdk` repository.
+The following instructions demonstrate `cosmovisor` using the simulation application (`simapp`) shipped with the Cosmos SDK's source code, upgrading a chain from `v0.53.7` to `v0.54.3`. This pair uses the in-repo `v053-to-v054` upgrade handler defined in `simapp/upgrades.go` on the `v0.54.x` line, so no custom code is required. Run these commands from within a clone of the `cosmos-sdk` repository.
### Chain Setup