504 lines
12 KiB
Markdown
504 lines
12 KiB
Markdown
# 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)](#app-wiring-changes-required)
|
|
* [Adding ProtocolPool Module (OPTIONAL)](#adding-protocolpool-module-optional)
|
|
* [ProtocolPool Manual Wiring](#protocolpool-manual-wiring)
|
|
* [ProtocolPool DI Wiring](#protocolpool-di-wiring)
|
|
* [Adding Epochs Module (OPTIONAL)](#adding-epochs-module-optional)
|
|
* [Epochs Manual Wiring](#epochs-manual-wiring)
|
|
* [Epochs DI Wiring](#epochs-di-wiring)
|
|
* [Enable Unordered Transactions (OPTIONAL)](#enable-unordered-transactions-optional)
|
|
* [Upgrade Handler](#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.
|
|
|
|
```go
|
|
app.ModuleManager.SetOrderPreBlockers(
|
|
upgradetypes.ModuleName,
|
|
authtypes.ModuleName, // NEW
|
|
)
|
|
```
|
|
|
|
## Adding ProtocolPool Module **OPTIONAL**
|
|
|
|
:::warning
|
|
|
|
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:
|
|
|
|
```go
|
|
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.
|
|
|
|
```go
|
|
maccPerms = map[string][]string{
|
|
// ...
|
|
protocolpooltypes.ModuleName: nil,
|
|
protocolpooltypes.ProtocolPoolEscrowAccount: nil,
|
|
}
|
|
```
|
|
|
|
Add the protocol pool keeper to your application struct.
|
|
|
|
```go
|
|
ProtocolPoolKeeper protocolpoolkeeper.Keeper
|
|
```
|
|
|
|
Add the store key:
|
|
|
|
```go
|
|
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.
|
|
|
|
```go
|
|
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:
|
|
|
|
```go
|
|
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:
|
|
|
|
```go
|
|
app.ModuleManager = module.NewManager(
|
|
// ...
|
|
protocolpool.NewAppModule(appCodec, app.ProtocolPoolKeeper, app.AccountKeeper, app.BankKeeper),
|
|
)
|
|
```
|
|
|
|
Add an entry for SetOrderBeginBlockers, SetOrderEndBlockers, SetOrderInitGenesis, and SetOrderExportGenesis.
|
|
|
|
```go
|
|
app.ModuleManager.SetOrderBeginBlockers(
|
|
// must come AFTER distribution.
|
|
distrtypes.ModuleName,
|
|
protocolpooltypes.ModuleName,
|
|
)
|
|
```
|
|
|
|
```go
|
|
app.ModuleManager.SetOrderEndBlockers(
|
|
// order does not matter.
|
|
protocolpooltypes.ModuleName,
|
|
)
|
|
```
|
|
|
|
```go
|
|
app.ModuleManager.SetOrderInitGenesis(
|
|
// order does not matter.
|
|
protocolpooltypes.ModuleName,
|
|
)
|
|
```
|
|
|
|
```go
|
|
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:
|
|
|
|
```go
|
|
protocolpoolkeeper "github.com/cosmos/cosmos-sdk/x/protocolpool/keeper"
|
|
```
|
|
|
|
Add the keeper to your application struct:
|
|
|
|
```go
|
|
ProtocolPoolKeeper protocolpoolkeeper.Keeper
|
|
```
|
|
|
|
Add the keeper to the depinject system:
|
|
|
|
```go
|
|
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:
|
|
|
|
```go
|
|
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:
|
|
|
|
```go
|
|
moduleAccPerms = []*authmodulev1.ModuleAccountPermission{
|
|
// ...
|
|
{Account: protocolpooltypes.ModuleName},
|
|
{Account: protocolpooltypes.ProtocolPoolEscrowAccount},
|
|
}
|
|
```
|
|
|
|
Next, add an entry for BeginBlockers, EndBlockers, InitGenesis, and ExportGenesis.
|
|
|
|
```go
|
|
BeginBlockers: []string{
|
|
// ...
|
|
// must be AFTER distribution.
|
|
distrtypes.ModuleName,
|
|
protocolpooltypes.ModuleName,
|
|
},
|
|
```
|
|
|
|
```go
|
|
EndBlockers: []string{
|
|
// ...
|
|
// order for protocolpool does not matter.
|
|
protocolpooltypes.ModuleName,
|
|
},
|
|
```
|
|
|
|
```go
|
|
InitGenesis: []string{
|
|
// ... must be AFTER distribution.
|
|
distrtypes.ModuleName,
|
|
protocolpooltypes.ModuleName,
|
|
},
|
|
```
|
|
|
|
```go
|
|
ExportGenesis: []string{
|
|
// ...
|
|
// Must be exported before x/bank.
|
|
protocolpooltypes.ModuleName,
|
|
banktypes.ModuleName,
|
|
},
|
|
```
|
|
|
|
Lastly, add an entry for protocolpool in the ModuleConfig.
|
|
|
|
```go
|
|
{
|
|
Name: protocolpooltypes.ModuleName,
|
|
Config: appconfig.WrapAny(&protocolpoolmodulev1.Module{}),
|
|
},
|
|
```
|
|
|
|
## Adding Epochs Module **OPTIONAL**
|
|
|
|
### Manual Wiring
|
|
|
|
Import the following:
|
|
|
|
```go
|
|
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:
|
|
|
|
```go
|
|
EpochsKeeper epochskeeper.Keeper
|
|
```
|
|
|
|
Add the store key:
|
|
|
|
```go
|
|
keys := storetypes.NewKVStoreKeys(
|
|
// ...
|
|
epochstypes.StoreKey,
|
|
)
|
|
```
|
|
|
|
Instantiate the keeper:
|
|
|
|
```go
|
|
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](https://github.com/cosmos/cosmos-sdk/blob/main/x/epochs/README.md)
|
|
|
|
```go
|
|
app.EpochsKeeper.SetHooks(
|
|
epochstypes.NewMultiEpochHooks(
|
|
// insert epoch hooks receivers here
|
|
app.SomeOtherModule
|
|
),
|
|
)
|
|
```
|
|
|
|
Add the epochs module to the module manager:
|
|
|
|
```go
|
|
app.ModuleManager = module.NewManager(
|
|
// ...
|
|
epochs.NewAppModule(appCodec, app.EpochsKeeper),
|
|
)
|
|
```
|
|
|
|
Add entries for SetOrderBeginBlockers and SetOrderInitGenesis:
|
|
|
|
```go
|
|
app.ModuleManager.SetOrderBeginBlockers(
|
|
// ...
|
|
epochstypes.ModuleName,
|
|
)
|
|
```
|
|
|
|
```go
|
|
app.ModuleManager.SetOrderInitGenesis(
|
|
// ...
|
|
epochstypes.ModuleName,
|
|
)
|
|
```
|
|
|
|
### DI Wiring
|
|
|
|
First, set up the keeper for the application.
|
|
|
|
Import the epochs keeper:
|
|
|
|
```go
|
|
epochskeeper "github.com/cosmos/cosmos-sdk/x/epochs/keeper"
|
|
```
|
|
|
|
Add the keeper to your application struct:
|
|
|
|
```go
|
|
EpochsKeeper epochskeeper.Keeper
|
|
```
|
|
|
|
Add the keeper to the depinject system:
|
|
|
|
```go
|
|
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:
|
|
|
|
```go
|
|
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:
|
|
|
|
```go
|
|
BeginBlockers: []string{
|
|
// ...
|
|
epochstypes.ModuleName,
|
|
},
|
|
```
|
|
|
|
```go
|
|
InitGenesis: []string{
|
|
// ...
|
|
epochstypes.ModuleName,
|
|
},
|
|
```
|
|
|
|
Lastly, add an entry for epochs in the ModuleConfig:
|
|
|
|
```go
|
|
{
|
|
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.
|
|
|
|
```go
|
|
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.
|
|
|
|
```go
|
|
{
|
|
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`.
|
|
|
|
```go
|
|
options := ante.HandlerOptions{
|
|
SigVerifyOptions: []ante.SigVerificationDecoratorOption{
|
|
// change below as needed.
|
|
ante.WithUnorderedTxGasCost(ante.DefaultUnorderedTxGasCost),
|
|
ante.WithMaxUnorderedTxTimeoutDuration(ante.DefaultMaxTimeoutDuration),
|
|
},
|
|
}
|
|
```
|
|
|
|
```go
|
|
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.
|
|
|
|
```go
|
|
// 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))
|
|
}
|
|
}
|
|
```
|