Upgrade Guide
This document provides a full guide for upgrading a Cosmos SDK chain from v0.50.x
to v0.53.x
.
This guide includes one required change and three optional features.
After completing this guide, applications will have:
- The
x/protocolpool
module - The
x/epochs
module - Unordered Transaction support
Table of Contents
- App Wiring Changes (REQUIRED)
- Adding ProtocolPool Module (OPTIONAL)
- Adding Epochs Module (OPTIONAL)
- Enable Unordered Transactions (OPTIONAL)
- Upgrade Handler
App Wiring Changes REQUIRED
The x/auth
module now contains a PreBlocker
that must be set in the module manager's SetOrderPreBlockers
method.
app.ModuleManager.SetOrderPreBlockers(
upgradetypes.ModuleName,
authtypes.ModuleName, // NEW
)
Adding ProtocolPool Module OPTIONAL
Using an external community pool such as x/protocolpool
will cause the following x/distribution
handlers to return an error:
QueryService
CommunityPool
MsgService
CommunityPoolSpend
FundCommunityPool
If your services depend on this functionality from x/distribution
, please update them to use either x/protocolpool
or your custom external community pool alternatives.
Manual Wiring
Import the following:
import (
// ...
"github.com/cosmos/cosmos-sdk/x/protocolpool"
protocolpoolkeeper "github.com/cosmos/cosmos-sdk/x/protocolpool/keeper"
protocolpooltypes "github.com/cosmos/cosmos-sdk/x/protocolpool/types"
)
Set the module account permissions.
maccPerms = map[string][]string{
// ...
protocolpooltypes.ModuleName: nil,
protocolpooltypes.ProtocolPoolEscrowAccount: nil,
}
Add the protocol pool keeper to your application struct.
ProtocolPoolKeeper protocolpoolkeeper.Keeper
Add the store key:
keys := storetypes.NewKVStoreKeys(
// ...
protocolpooltypes.StoreKey,
)
Instantiate the keeper.
Make sure to do this before the distribution module instantiation, as you will pass the keeper there next.
app.ProtocolPoolKeeper = protocolpoolkeeper.NewKeeper(
appCodec,
runtime.NewKVStoreService(keys[protocolpooltypes.StoreKey]),
app.AccountKeeper,
app.BankKeeper,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)
Pass the protocolpool keeper to the distribution keeper:
app.DistrKeeper = distrkeeper.NewKeeper(
appCodec,
runtime.NewKVStoreService(keys[distrtypes.StoreKey]),
app.AccountKeeper,
app.BankKeeper,
app.StakingKeeper,
authtypes.FeeCollectorName,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
distrkeeper.WithExternalCommunityPool(app.ProtocolPoolKeeper), // NEW
)
Add the protocolpool module to the module manager:
app.ModuleManager = module.NewManager(
// ...
protocolpool.NewAppModule(appCodec, app.ProtocolPoolKeeper, app.AccountKeeper, app.BankKeeper),
)
Add an entry for SetOrderBeginBlockers, SetOrderEndBlockers, SetOrderInitGenesis, and SetOrderExportGenesis.
app.ModuleManager.SetOrderBeginBlockers(
// must come AFTER distribution.
distrtypes.ModuleName,
protocolpooltypes.ModuleName,
)
app.ModuleManager.SetOrderEndBlockers(
// order does not matter.
protocolpooltypes.ModuleName,
)
app.ModuleManager.SetOrderInitGenesis(
// order does not matter.
protocolpooltypes.ModuleName,
)
app.ModuleManager.SetOrderInitGenesis(
protocolpooltypes.ModuleName, // must be exported before bank.
banktypes.ModuleName,
)
DI Wiring
Note: as long as an external community pool keeper (here, x/protocolpool
) is wired in DI configs, x/distribution
will automatically use it for its external pool.
First, set up the keeper for the application.
Import the protocolpool keeper:
protocolpoolkeeper "github.com/cosmos/cosmos-sdk/x/protocolpool/keeper"
Add the keeper to your application struct:
ProtocolPoolKeeper protocolpoolkeeper.Keeper
Add the keeper to the depinject system:
depinject.Inject(
appConfig,
&appBuilder,
&app.appCodec,
&app.legacyAmino,
&app.txConfig,
&app.interfaceRegistry,
// ... other modules
&app.ProtocolPoolKeeper, // NEW MODULE!
)
Next, set up configuration for the module.
Import the following:
import (
protocolpoolmodulev1 "cosmossdk.io/api/cosmos/protocolpool/module/v1"
_ "github.com/cosmos/cosmos-sdk/x/protocolpool" // import for side-effects
protocolpooltypes "github.com/cosmos/cosmos-sdk/x/protocolpool/types"
)
The protocolpool module has module accounts that handle funds. Add them to the module account permission configuration:
moduleAccPerms = []*authmodulev1.ModuleAccountPermission{
// ...
{Account: protocolpooltypes.ModuleName},
{Account: protocolpooltypes.ProtocolPoolEscrowAccount},
}
Next, add an entry for BeginBlockers, EndBlockers, InitGenesis, and ExportGenesis.
BeginBlockers: []string{
// ...
// must be AFTER distribution.
distrtypes.ModuleName,
protocolpooltypes.ModuleName,
},
EndBlockers: []string{
// ...
// order for protocolpool does not matter.
protocolpooltypes.ModuleName,
},
InitGenesis: []string{
// ... must be AFTER distribution.
distrtypes.ModuleName,
protocolpooltypes.ModuleName,
},
ExportGenesis: []string{
// ...
// Must be exported before x/bank.
protocolpooltypes.ModuleName,
banktypes.ModuleName,
},
Lastly, add an entry for protocolpool in the ModuleConfig.
{
Name: protocolpooltypes.ModuleName,
Config: appconfig.WrapAny(&protocolpoolmodulev1.Module{}),
},
Adding Epochs Module OPTIONAL
Manual Wiring
Import the following:
import (
// ...
"github.com/cosmos/cosmos-sdk/x/epochs"
epochskeeper "github.com/cosmos/cosmos-sdk/x/epochs/keeper"
epochstypes "github.com/cosmos/cosmos-sdk/x/epochs/types"
)
Add the epochs keeper to your application struct:
EpochsKeeper epochskeeper.Keeper
Add the store key:
keys := storetypes.NewKVStoreKeys(
// ...
epochstypes.StoreKey,
)
Instantiate the keeper:
app.EpochsKeeper = epochskeeper.NewKeeper(
runtime.NewKVStoreService(keys[epochstypes.StoreKey]),
appCodec,
)
Set up hooks for the epochs keeper:
To learn how to write hooks for the epoch keeper, see the x/epoch README
app.EpochsKeeper.SetHooks(
epochstypes.NewMultiEpochHooks(
// insert epoch hooks receivers here
app.SomeOtherModule
),
)
Add the epochs module to the module manager:
app.ModuleManager = module.NewManager(
// ...
epochs.NewAppModule(appCodec, app.EpochsKeeper),
)
Add entries for SetOrderBeginBlockers and SetOrderInitGenesis:
app.ModuleManager.SetOrderBeginBlockers(
// ...
epochstypes.ModuleName,
)
app.ModuleManager.SetOrderInitGenesis(
// ...
epochstypes.ModuleName,
)
DI Wiring
First, set up the keeper for the application.
Import the epochs keeper:
epochskeeper "github.com/cosmos/cosmos-sdk/x/epochs/keeper"
Add the keeper to your application struct:
EpochsKeeper epochskeeper.Keeper
Add the keeper to the depinject system:
depinject.Inject(
appConfig,
&appBuilder,
&app.appCodec,
&app.legacyAmino,
&app.txConfig,
&app.interfaceRegistry,
// ... other modules
&app.EpochsKeeper, // NEW MODULE!
)
Next, set up configuration for the module.
Import the following:
import (
epochsmodulev1 "cosmossdk.io/api/cosmos/epochs/module/v1"
_ "github.com/cosmos/cosmos-sdk/x/epochs" // import for side-effects
epochstypes "github.com/cosmos/cosmos-sdk/x/epochs/types"
)
Add an entry for BeginBlockers and InitGenesis:
BeginBlockers: []string{
// ...
epochstypes.ModuleName,
},
InitGenesis: []string{
// ...
epochstypes.ModuleName,
},
Lastly, add an entry for epochs in the ModuleConfig:
{
Name: epochstypes.ModuleName,
Config: appconfig.WrapAny(&epochsmodulev1.Module{}),
},
Enable Unordered Transactions OPTIONAL
To enable unordered transaction support on an application, the x/auth
keeper must be supplied with the WithUnorderedTransactions
option.
Note that unordered transactions require sequence values to be zero, and will FAIL if a non-zero sequence value is set. Please ensure no sequence value is set when submitting an unordered transaction. Services that rely on prior assumptions about sequence values should be updated to handle unordered transactions. Services should be aware that when the transaction is unordered, the transaction sequence will always be zero.
app.AccountKeeper = authkeeper.NewAccountKeeper(
appCodec,
runtime.NewKVStoreService(keys[authtypes.StoreKey]),
authtypes.ProtoBaseAccount,
maccPerms,
authcodec.NewBech32Codec(sdk.Bech32MainPrefix),
sdk.Bech32MainPrefix,
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
authkeeper.WithUnorderedTransactions(true), // new option!
)
If using dependency injection, update the auth module config.
{
Name: authtypes.ModuleName,
Config: appconfig.WrapAny(&authmodulev1.Module{
Bech32Prefix: "cosmos",
ModuleAccountPermissions: moduleAccPerms,
EnableUnorderedTransactions: true, // remove this line if you do not want unordered transactions.
}),
},
By default, unordered transactions use a transaction timeout duration of 10 minutes and a default gas charge of 2240 gas units.
To modify these default values, pass in the corresponding options to the new SigVerifyOptions
field in x/auth's
ante.HandlerOptions
.
options := ante.HandlerOptions{
SigVerifyOptions: []ante.SigVerificationDecoratorOption{
// change below as needed.
ante.WithUnorderedTxGasCost(ante.DefaultUnorderedTxGasCost),
ante.WithMaxUnorderedTxTimeoutDuration(ante.DefaultMaxTimoutDuration),
},
}
anteDecorators := []sdk.AnteDecorator{
// ... other decorators ...
ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler, options.SigVerifyOptions...), // supply new options
}
Upgrade Handler
The upgrade handler only requires adding the store upgrades for the modules added above.
If your application is not adding x/protocolpool
or x/epochs
, you do not need to add the store upgrade.
// UpgradeName defines the on-chain upgrade name for the sample SimApp upgrade
// from v050 to v053.
//
// NOTE: This upgrade defines a reference implementation of what an upgrade
// could look like when an application is migrating from Cosmos SDK version
// v0.50.x to v0.53.x.
const UpgradeName = "v050-to-v053"
func (app SimApp) RegisterUpgradeHandlers() {
app.UpgradeKeeper.SetUpgradeHandler(
UpgradeName,
func(ctx context.Context, _ upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) {
return app.ModuleManager.RunMigrations(ctx, app.Configurator(), fromVM)
},
)
upgradeInfo, err := app.UpgradeKeeper.ReadUpgradeInfoFromDisk()
if err != nil {
panic(err)
}
if upgradeInfo.Name == UpgradeName && !app.UpgradeKeeper.IsSkipHeight(upgradeInfo.Height) {
storeUpgrades := storetypes.StoreUpgrades{
Added: []string{
epochstypes.ModuleName, // if not adding x/epochs to your chain, remove this line.
protocolpooltypes.ModuleName, // if not adding x/protocolpool to your chain, remove this line.
},
}
// configure store loader that checks if version == upgradeHeight and applies store upgrades
app.SetStoreLoader(upgradetypes.UpgradeStoreLoader(upgradeInfo.Height, &storeUpgrades))
}
}