Skip to content

Commit

Permalink
refactor(x/upgrade)!: use KVStoreService and context.Context (#16227)
Browse files Browse the repository at this point in the history
  • Loading branch information
facundomedica authored Jun 5, 2023
1 parent 5089313 commit bbab968
Show file tree
Hide file tree
Showing 24 changed files with 512 additions and 321 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### API Breaking Changes

* (x/slashing) [#16246](https://github.com/cosmos/cosmos-sdk/issues/16246) `NewKeeper` now takes a `KVStoreService` instead of a `StoreKey`, and methods in the `Keeper` now take a `context.Context` instead of a `sdk.Context` and return an `error`. `GetValidatorSigningInfo` now returns an error instead of a `found bool`, the error can be `nil` (found), `ErrNoSigningInfoFound` (not found) and any other error.
* (module) [#16227](https://github.com/cosmos/cosmos-sdk/issues/16227) `manager.RunMigrations()` now take a `context.Context` instead of a `sdk.Context`.
* (x/mint) [#16179](https://github.com/cosmos/cosmos-sdk/issues/16179) `NewKeeper` now takes a `KVStoreService` instead of a `StoreKey`, and methods in the `Keeper` now take a `context.Context` instead of a `sdk.Context` and return an `error`.
* (x/crisis) [#16216](https://github.com/cosmos/cosmos-sdk/issues/16216) `NewKeeper` now takes a `KVStoreService` instead of a `StoreKey`, methods in the `Keeper` now take a `context.Context` instead of a `sdk.Context` and return an `error` instead of panicking.
* (x/gov) [#15988](https://github.com/cosmos/cosmos-sdk/issues/15988) `NewKeeper` now takes a `KVStoreService` instead of a `StoreKey`, methods in the `Keeper` now take a `context.Context` instead of a `sdk.Context` and return an `error` (instead of panicking or returning a `found bool`). Iterators callback functions now return an error instead of a `bool`.
Expand Down
2 changes: 2 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ The following modules `NewKeeper` function now take a `KVStoreService` instead o
* `x/mint`
* `x/nft`
* `x/slashing`
* `x/upgrade`

Users manually wiring their chain need to use the `runtime.NewKVStoreService` method to create a `KVStoreService` from a `StoreKey`:

Expand All @@ -111,6 +112,7 @@ The following modules' `Keeper` methods now take in a `context.Context` instead
* `x/evidence`
* `x/gov`
* `x/slashing`
* `x/upgrade`

**Users using depinject do not need any changes, this is automatically done for them.**

Expand Down
2 changes: 1 addition & 1 deletion simapp/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ func NewSimApp(
}
homePath := cast.ToString(appOpts.Get(flags.FlagHome))
// set the governance module account as the authority for conducting upgrades
app.UpgradeKeeper = upgradekeeper.NewKeeper(skipUpgradeHeights, keys[upgradetypes.StoreKey], appCodec, homePath, app.BaseApp, authtypes.NewModuleAddress(govtypes.ModuleName).String())
app.UpgradeKeeper = upgradekeeper.NewKeeper(skipUpgradeHeights, runtime.NewKVStoreService(keys[upgradetypes.StoreKey]), appCodec, homePath, app.BaseApp, authtypes.NewModuleAddress(govtypes.ModuleName).String())

// Register the proposal types
// Deprecated: Avoid adding new handlers, instead use the new proposal flow
Expand Down
3 changes: 2 additions & 1 deletion simapp/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,8 @@ func TestUpgradeStateOnGenesis(t *testing.T) {

// make sure the upgrade keeper has version map in state
ctx := app.NewContext(false)
vm := app.UpgradeKeeper.GetModuleVersionMap(ctx)
vm, err := app.UpgradeKeeper.GetModuleVersionMap(ctx)
require.NoError(t, err)
for v, i := range app.ModuleManager.Modules {
if i, ok := i.(module.HasConsensusVersion); ok {
require.Equal(t, vm[v], i.ConsensusVersion())
Expand Down
5 changes: 3 additions & 2 deletions simapp/upgrades.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package simapp

import (
"context"

storetypes "cosmossdk.io/store/types"
upgradetypes "cosmossdk.io/x/upgrade/types"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
)

Expand All @@ -19,7 +20,7 @@ const UpgradeName = "v047-to-v050"
func (app SimApp) RegisterUpgradeHandlers() {
app.UpgradeKeeper.SetUpgradeHandler(
UpgradeName,
func(ctx sdk.Context, _ upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) {
func(ctx context.Context, _ upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) {
return app.ModuleManager.RunMigrations(ctx, app.Configurator(), fromVM)
},
)
Expand Down
7 changes: 5 additions & 2 deletions tests/e2e/upgrade/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,13 @@ func (s *E2ETestSuite) TestModuleVersionsCLI() {
// avoid printing as yaml from CLI command
clientCtx.OutputFormat = "JSON"

vm := s.upgradeKeeper.GetModuleVersionMap(s.ctx)
mv := s.upgradeKeeper.GetModuleVersions(s.ctx)
vm, err := s.upgradeKeeper.GetModuleVersionMap(s.ctx)
s.Require().NoError(err)
s.Require().NotEmpty(vm)

mv, err := s.upgradeKeeper.GetModuleVersions(s.ctx)
s.Require().NoError(err)

for _, tc := range testCases {
s.Run(fmt.Sprintf("Case %s", tc.msg), func() {
expect := mv
Expand Down
13 changes: 7 additions & 6 deletions types/module/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,7 @@ type VersionMap map[string]uint64
// Example:
//
// cfg := module.NewConfigurator(...)
// app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) {
// app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx context.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) {
// return app.mm.RunMigrations(ctx, cfg, fromVM)
// })
//
Expand All @@ -625,7 +625,7 @@ type VersionMap map[string]uint64
// Example:
//
// cfg := module.NewConfigurator(...)
// app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx sdk.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) {
// app.UpgradeKeeper.SetUpgradeHandler("my-plan", func(ctx context.Context, plan upgradetypes.Plan, fromVM module.VersionMap) (module.VersionMap, error) {
// // Assume "foo" is a new module.
// // `fromVM` is fetched from existing x/upgrade store. Since foo didn't exist
// // before this upgrade, `v, exists := fromVM["foo"]; exists == false`, and RunMigration will by default
Expand All @@ -638,7 +638,7 @@ type VersionMap map[string]uint64
// })
//
// Please also refer to https://docs.cosmos.network/main/core/upgrade for more information.
func (m Manager) RunMigrations(ctx sdk.Context, cfg Configurator, fromVM VersionMap) (VersionMap, error) {
func (m Manager) RunMigrations(ctx context.Context, cfg Configurator, fromVM VersionMap) (VersionMap, error) {
c, ok := cfg.(*configurator)
if !ok {
return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidType, "expected %T, got %T", &configurator{}, cfg)
Expand All @@ -648,6 +648,7 @@ func (m Manager) RunMigrations(ctx sdk.Context, cfg Configurator, fromVM Version
modules = DefaultMigrationsOrder(m.ModuleNames())
}

sdkCtx := sdk.UnwrapSDKContext(ctx)
updatedVM := VersionMap{}
for _, moduleName := range modules {
module := m.Modules[moduleName]
Expand All @@ -666,14 +667,14 @@ func (m Manager) RunMigrations(ctx sdk.Context, cfg Configurator, fromVM Version
// 2. An existing chain is upgrading from version < 0.43 to v0.43+ for the first time.
// In this case, all modules have yet to be added to x/upgrade's VersionMap store.
if exists {
err := c.runModuleMigrations(ctx, moduleName, fromVersion, toVersion)
err := c.runModuleMigrations(sdkCtx, moduleName, fromVersion, toVersion)
if err != nil {
return nil, err
}
} else {
ctx.Logger().Info(fmt.Sprintf("adding a new module: %s", moduleName))
sdkCtx.Logger().Info(fmt.Sprintf("adding a new module: %s", moduleName))
if module, ok := m.Modules[moduleName].(HasGenesis); ok {
moduleValUpdates := module.InitGenesis(ctx, c.cdc, module.DefaultGenesis(c.cdc))
moduleValUpdates := module.InitGenesis(sdkCtx, c.cdc, module.DefaultGenesis(c.cdc))
// The module manager assumes only one module will update the
// validator set, and it can't be a new module.
if len(moduleValUpdates) > 0 {
Expand Down
4 changes: 4 additions & 0 deletions x/upgrade/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,7 @@ Ref: https://keepachangelog.com/en/1.0.0/

* [#14880](https://github.com/cosmos/cosmos-sdk/pull/14880) Switch from using gov v1beta1 to gov v1 in upgrade CLIs.
* [#14764](https://github.com/cosmos/cosmos-sdk/pull/14764) The `x/upgrade` module is extracted to have a separate go.mod file which allows it be a standalone module.

### API Breaking

* (x/upgrade) [#16227](https://github.com/cosmos/cosmos-sdk/issues/16227) `NewKeeper` now takes a `KVStoreService` instead of a `StoreKey`, methods in the `Keeper` now take a `context.Context` instead of a `sdk.Context` and return an `error`. `UpgradeHandler` now receives a `context.Context`. `GetUpgradedClient`, `GetUpgradedConsensusState`, `GetUpgradePlan` now return a specific error for "not found".
59 changes: 38 additions & 21 deletions x/upgrade/abci.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package upgrade

import (
"context"
"errors"
"fmt"
"time"

Expand All @@ -20,10 +22,16 @@ import (
// The purpose is to ensure the binary is switched EXACTLY at the desired block, and to allow
// a migration to be executed if needed upon this switch (migration defined in the new binary)
// skipUpgradeHeightArray is a set of block heights for which the upgrade must be skipped
func BeginBlocker(k *keeper.Keeper, ctx sdk.Context) {
func BeginBlocker(ctx context.Context, k *keeper.Keeper) error {
defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker)

plan, found := k.GetUpgradePlan(ctx)
sdkCtx := sdk.UnwrapSDKContext(ctx)
blockHeight := sdkCtx.HeaderInfo().Height
plan, err := k.GetUpgradePlan(ctx)
if err != nil && !errors.Is(err, types.ErrNoUpgradePlanFound) {
return err
}
found := err == nil

if !k.DowngradeVerified() {
k.SetDowngradeVerified(true)
Expand All @@ -32,66 +40,75 @@ func BeginBlocker(k *keeper.Keeper, ctx sdk.Context) {
// 1. If there is no scheduled upgrade.
// 2. If the plan is not ready.
// 3. If the plan is ready and skip upgrade height is set for current height.
if !found || !plan.ShouldExecute(ctx) || (plan.ShouldExecute(ctx) && k.IsSkipHeight(ctx.HeaderInfo().Height)) {
lastAppliedPlan, _ := k.GetLastCompletedUpgrade(ctx)
if !found || !plan.ShouldExecute(blockHeight) || (plan.ShouldExecute(blockHeight) && k.IsSkipHeight(blockHeight)) {
lastAppliedPlan, _, err := k.GetLastCompletedUpgrade(ctx)
if err != nil {
return err
}

if lastAppliedPlan != "" && !k.HasHandler(lastAppliedPlan) {
var appVersion uint64

cp := ctx.ConsensusParams()
cp := sdkCtx.ConsensusParams()
if cp.Version != nil {
appVersion = cp.Version.App
}

panic(fmt.Sprintf("Wrong app version %d, upgrade handler is missing for %s upgrade plan", appVersion, lastAppliedPlan))
return fmt.Errorf("wrong app version %d, upgrade handler is missing for %s upgrade plan", appVersion, lastAppliedPlan)
}
}
}

if !found {
return
return nil
}
logger := ctx.Logger()

logger := k.Logger(ctx)

// To make sure clear upgrade is executed at the same block
if plan.ShouldExecute(ctx) {
if plan.ShouldExecute(blockHeight) {
// If skip upgrade has been set for current height, we clear the upgrade plan
if k.IsSkipHeight(ctx.HeaderInfo().Height) {
if k.IsSkipHeight(blockHeight) {
skipUpgradeMsg := fmt.Sprintf("UPGRADE \"%s\" SKIPPED at %d: %s", plan.Name, plan.Height, plan.Info)
logger.Info(skipUpgradeMsg)

// Clear the upgrade plan at current height
k.ClearUpgradePlan(ctx)
return
return k.ClearUpgradePlan(ctx)
}

// Prepare shutdown if we don't have an upgrade handler for this upgrade name (meaning this software is out of date)
if !k.HasHandler(plan.Name) {
// Write the upgrade info to disk. The UpgradeStoreLoader uses this info to perform or skip
// store migrations.
err := k.DumpUpgradeInfoToDisk(ctx.HeaderInfo().Height, plan)
err := k.DumpUpgradeInfoToDisk(blockHeight, plan)
if err != nil {
panic(fmt.Errorf("unable to write upgrade info to filesystem: %s", err.Error()))
return fmt.Errorf("unable to write upgrade info to filesystem: %s", err.Error())
}

upgradeMsg := BuildUpgradeNeededMsg(plan)
logger.Error(upgradeMsg)
panic(upgradeMsg)

// Returning an error will end up in a panic
return fmt.Errorf(upgradeMsg)
}

// We have an upgrade handler for this upgrade name, so apply the upgrade
ctx.Logger().Info(fmt.Sprintf("applying upgrade \"%s\" at %s", plan.Name, plan.DueAt()))
ctx = ctx.WithBlockGasMeter(storetypes.NewInfiniteGasMeter())
k.ApplyUpgrade(ctx, plan)
return
logger.Info(fmt.Sprintf("applying upgrade \"%s\" at %s", plan.Name, plan.DueAt()))
sdkCtx = sdkCtx.WithBlockGasMeter(storetypes.NewInfiniteGasMeter())
return k.ApplyUpgrade(sdkCtx, plan)
}

// if we have a pending upgrade, but it is not yet time, make sure we did not
// set the handler already
if k.HasHandler(plan.Name) {
downgradeMsg := fmt.Sprintf("BINARY UPDATED BEFORE TRIGGER! UPGRADE \"%s\" - in binary but not executed on chain. Downgrade your binary", plan.Name)
ctx.Logger().Error(downgradeMsg)
panic(downgradeMsg)
logger.Error(downgradeMsg)

// Returning an error will end up in a panic
return fmt.Errorf(downgradeMsg)
}

return nil
}

// BuildUpgradeNeededMsg prints the message that notifies that an upgrade is needed.
Expand Down
Loading

0 comments on commit bbab968

Please sign in to comment.