564 lines
18 KiB
Go
564 lines
18 KiB
Go
package keeper_test
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
cmtproto "github.com/cometbft/cometbft/api/cometbft/types/v1"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/mock/gomock"
|
|
|
|
"cosmossdk.io/core/appmodule"
|
|
"cosmossdk.io/core/header"
|
|
"cosmossdk.io/core/server"
|
|
coretesting "cosmossdk.io/core/testing"
|
|
"cosmossdk.io/log"
|
|
storetypes "cosmossdk.io/store/types"
|
|
"cosmossdk.io/x/upgrade"
|
|
"cosmossdk.io/x/upgrade/keeper"
|
|
upgradetestutil "cosmossdk.io/x/upgrade/testutil"
|
|
"cosmossdk.io/x/upgrade/types"
|
|
|
|
"github.com/cosmos/cosmos-sdk/baseapp"
|
|
addresscodec "github.com/cosmos/cosmos-sdk/codec/address"
|
|
codectestutil "github.com/cosmos/cosmos-sdk/codec/testutil"
|
|
"github.com/cosmos/cosmos-sdk/runtime"
|
|
"github.com/cosmos/cosmos-sdk/testutil"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
|
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
|
|
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
|
)
|
|
|
|
const govModuleName = "gov"
|
|
|
|
type TestSuite struct {
|
|
preModule appmodule.HasPreBlocker
|
|
keeper *keeper.Keeper
|
|
ctx sdk.Context
|
|
baseApp *baseapp.BaseApp
|
|
encCfg moduletestutil.TestEncodingConfig
|
|
|
|
key storetypes.StoreKey
|
|
env appmodule.Environment
|
|
}
|
|
|
|
func (s *TestSuite) VerifyDoUpgrade(t *testing.T) {
|
|
t.Helper()
|
|
t.Log("Verify that a panic happens at the upgrade height")
|
|
newCtx := s.ctx.WithHeaderInfo(header.Info{Height: s.ctx.HeaderInfo().Height + 1, Time: time.Now()})
|
|
|
|
err := s.preModule.PreBlock(newCtx)
|
|
require.ErrorContains(t, err, "UPGRADE \"test\" NEEDED at height: 11: ")
|
|
|
|
t.Log("Verify that the upgrade can be successfully applied with a handler")
|
|
s.keeper.SetUpgradeHandler("test", func(ctx context.Context, plan types.Plan, vm appmodule.VersionMap) (appmodule.VersionMap, error) {
|
|
return vm, nil
|
|
})
|
|
|
|
err = s.preModule.PreBlock(newCtx)
|
|
require.NoError(t, err)
|
|
|
|
s.VerifyCleared(t, newCtx)
|
|
}
|
|
|
|
func (s *TestSuite) VerifyDoUpgradeWithCtx(t *testing.T, newCtx sdk.Context, proposalName string) {
|
|
t.Helper()
|
|
t.Log("Verify that a panic happens at the upgrade height")
|
|
|
|
err := s.preModule.PreBlock(newCtx)
|
|
require.ErrorContains(t, err, "UPGRADE \""+proposalName+"\" NEEDED at height: ")
|
|
|
|
t.Log("Verify that the upgrade can be successfully applied with a handler")
|
|
s.keeper.SetUpgradeHandler(proposalName, func(ctx context.Context, plan types.Plan, vm appmodule.VersionMap) (appmodule.VersionMap, error) {
|
|
return vm, nil
|
|
})
|
|
|
|
err = s.preModule.PreBlock(newCtx)
|
|
require.NoError(t, err)
|
|
|
|
s.VerifyCleared(t, newCtx)
|
|
}
|
|
|
|
func (s *TestSuite) VerifyCleared(t *testing.T, newCtx sdk.Context) {
|
|
t.Helper()
|
|
t.Log("Verify that the upgrade plan has been cleared")
|
|
_, err := s.keeper.GetUpgradePlan(newCtx)
|
|
require.ErrorIs(t, err, types.ErrNoUpgradePlanFound)
|
|
}
|
|
|
|
func (s *TestSuite) VerifyNotDone(t *testing.T, newCtx sdk.Context, name string) {
|
|
t.Helper()
|
|
t.Log("Verify that upgrade was not done")
|
|
height, err := s.keeper.GetDoneHeight(newCtx, name)
|
|
require.Zero(t, height)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func (s *TestSuite) VerifyDone(t *testing.T, newCtx sdk.Context, name string) {
|
|
t.Helper()
|
|
t.Log("Verify that the upgrade plan has been executed")
|
|
height, err := s.keeper.GetDoneHeight(newCtx, name)
|
|
require.NotZero(t, height)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func (s *TestSuite) VerifySet(t *testing.T, skipUpgradeHeights map[int64]bool) {
|
|
t.Helper()
|
|
t.Log("Verify if the skip upgrade has been set")
|
|
|
|
for k := range skipUpgradeHeights {
|
|
require.True(t, s.keeper.IsSkipHeight(k))
|
|
}
|
|
}
|
|
|
|
func setupTest(t *testing.T, height int64, skip map[int64]bool) *TestSuite {
|
|
t.Helper()
|
|
key := storetypes.NewKVStoreKey(types.StoreKey)
|
|
s := TestSuite{
|
|
key: key,
|
|
}
|
|
s.encCfg = moduletestutil.MakeTestEncodingConfig(codectestutil.CodecOptions{}, upgrade.AppModule{})
|
|
|
|
testCtx := testutil.DefaultContextWithDB(t, key, storetypes.NewTransientStoreKey("transient_test"))
|
|
s.baseApp = baseapp.NewBaseApp(
|
|
"upgrade",
|
|
log.NewNopLogger(),
|
|
testCtx.DB,
|
|
s.encCfg.TxConfig.TxDecoder(),
|
|
)
|
|
|
|
storeService := runtime.NewKVStoreService(key)
|
|
s.env = runtime.NewEnvironment(storeService, coretesting.NewNopLogger(), runtime.EnvWithMsgRouterService(s.baseApp.MsgServiceRouter()), runtime.EnvWithQueryRouterService(s.baseApp.GRPCQueryRouter()))
|
|
|
|
s.baseApp.SetParamStore(¶mStore{params: cmtproto.ConsensusParams{Version: &cmtproto.VersionParams{App: 1}}})
|
|
s.baseApp.SetVersionModifier(newMockedVersionModifier(1))
|
|
|
|
authority, err := addresscodec.NewBech32Codec("cosmos").BytesToString(authtypes.NewModuleAddress(govModuleName))
|
|
require.NoError(t, err)
|
|
|
|
ctrl := gomock.NewController(t)
|
|
ck := upgradetestutil.NewMockConsensusKeeper(ctrl)
|
|
s.keeper = keeper.NewKeeper(s.env, skip, s.encCfg.Codec, t.TempDir(), s.baseApp, authority, ck)
|
|
|
|
s.ctx = testCtx.Ctx.WithHeaderInfo(header.Info{Time: time.Now(), Height: height})
|
|
|
|
s.preModule = upgrade.NewAppModule(s.keeper)
|
|
return &s
|
|
}
|
|
|
|
func newMockedVersionModifier(startingVersion uint64) server.VersionModifier {
|
|
return &mockedVersionModifier{version: startingVersion}
|
|
}
|
|
|
|
type mockedVersionModifier struct {
|
|
version uint64
|
|
}
|
|
|
|
func (m *mockedVersionModifier) SetAppVersion(ctx context.Context, u uint64) error {
|
|
m.version = u
|
|
return nil
|
|
}
|
|
|
|
func (m *mockedVersionModifier) AppVersion(ctx context.Context) (uint64, error) {
|
|
return m.version, nil
|
|
}
|
|
|
|
func TestRequireFutureBlock(t *testing.T) {
|
|
s := setupTest(t, 10, map[int64]bool{})
|
|
err := s.keeper.ScheduleUpgrade(s.ctx, types.Plan{Name: "test", Height: s.ctx.HeaderInfo().Height - 1})
|
|
require.Error(t, err)
|
|
require.True(t, errors.Is(err, sdkerrors.ErrInvalidRequest), err)
|
|
}
|
|
|
|
func TestDoHeightUpgrade(t *testing.T) {
|
|
s := setupTest(t, 10, map[int64]bool{})
|
|
t.Log("Verify can schedule an upgrade")
|
|
err := s.keeper.ScheduleUpgrade(s.ctx, types.Plan{Name: "test", Height: s.ctx.HeaderInfo().Height + 1})
|
|
require.NoError(t, err)
|
|
|
|
s.VerifyDoUpgrade(t)
|
|
}
|
|
|
|
func TestCanOverwriteScheduleUpgrade(t *testing.T) {
|
|
s := setupTest(t, 10, map[int64]bool{})
|
|
t.Log("Can overwrite plan")
|
|
err := s.keeper.ScheduleUpgrade(s.ctx, types.Plan{Name: "bad_test", Height: s.ctx.HeaderInfo().Height + 10})
|
|
require.NoError(t, err)
|
|
err = s.keeper.ScheduleUpgrade(s.ctx, types.Plan{Name: "test", Height: s.ctx.HeaderInfo().Height + 1})
|
|
require.NoError(t, err)
|
|
|
|
s.VerifyDoUpgrade(t)
|
|
}
|
|
|
|
func TestHaltIfTooNew(t *testing.T) {
|
|
s := setupTest(t, 10, map[int64]bool{})
|
|
t.Log("Verify that we don't panic with registered plan not in database at all")
|
|
var called int
|
|
s.keeper.SetUpgradeHandler("future", func(_ context.Context, _ types.Plan, vm appmodule.VersionMap) (appmodule.VersionMap, error) {
|
|
called++
|
|
return vm, nil
|
|
})
|
|
|
|
newCtx := s.ctx.WithHeaderInfo(header.Info{Height: s.ctx.HeaderInfo().Height + 1, Time: time.Now()})
|
|
err := s.preModule.PreBlock(newCtx)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 0, called)
|
|
|
|
t.Log("Verify we error if we have a registered handler ahead of time")
|
|
err = s.keeper.ScheduleUpgrade(s.ctx, types.Plan{Name: "future", Height: s.ctx.HeaderInfo().Height + 3})
|
|
require.NoError(t, err)
|
|
err = s.preModule.PreBlock(newCtx)
|
|
require.EqualError(t, err, "BINARY UPDATED BEFORE TRIGGER! UPGRADE \"future\" - in binary but not executed on chain. Downgrade your binary")
|
|
require.Equal(t, 0, called)
|
|
|
|
t.Log("Verify we no longer panic if the plan is on time")
|
|
|
|
futCtx := s.ctx.WithHeaderInfo(header.Info{Height: s.ctx.HeaderInfo().Height + 3, Time: time.Now()})
|
|
err = s.preModule.PreBlock(futCtx)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1, called)
|
|
|
|
s.VerifyCleared(t, futCtx)
|
|
}
|
|
|
|
func TestCanClear(t *testing.T) {
|
|
s := setupTest(t, 10, map[int64]bool{})
|
|
t.Log("Verify upgrade is scheduled")
|
|
err := s.keeper.ScheduleUpgrade(s.ctx, types.Plan{Name: "test", Height: s.ctx.HeaderInfo().Height + 100})
|
|
require.NoError(t, err)
|
|
|
|
err = s.keeper.ClearUpgradePlan(s.ctx)
|
|
require.NoError(t, err)
|
|
|
|
s.VerifyCleared(t, s.ctx)
|
|
}
|
|
|
|
func TestCantApplySameUpgradeTwice(t *testing.T) {
|
|
s := setupTest(t, 10, map[int64]bool{})
|
|
height := s.ctx.HeaderInfo().Height + 1
|
|
err := s.keeper.ScheduleUpgrade(s.ctx, types.Plan{Name: "test", Height: height})
|
|
require.NoError(t, err)
|
|
s.VerifyDoUpgrade(t)
|
|
t.Log("Verify an executed upgrade \"test\" can't be rescheduled")
|
|
err = s.keeper.ScheduleUpgrade(s.ctx, types.Plan{Name: "test", Height: height})
|
|
require.Error(t, err)
|
|
require.True(t, errors.Is(err, sdkerrors.ErrInvalidRequest), err)
|
|
}
|
|
|
|
func TestNoSpuriousUpgrades(t *testing.T) {
|
|
s := setupTest(t, 10, map[int64]bool{})
|
|
t.Log("Verify that no upgrade panic is triggered in the BeginBlocker when we haven't scheduled an upgrade")
|
|
err := s.preModule.PreBlock(s.ctx)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestPlanStringer(t *testing.T) {
|
|
require.Equal(t, "name:\"test\" time:<seconds:-62135596800 > height:100 ", (&types.Plan{Name: "test", Height: 100, Info: ""}).String())
|
|
require.Equal(t, `name:"test" time:<seconds:-62135596800 > height:100 `, (&types.Plan{Name: "test", Height: 100, Info: ""}).String())
|
|
}
|
|
|
|
func TestContains(t *testing.T) {
|
|
var skipOne int64 = 11
|
|
s := setupTest(t, 10, map[int64]bool{skipOne: true})
|
|
|
|
s.VerifySet(t, map[int64]bool{skipOne: true})
|
|
t.Log("case where array contains the element")
|
|
require.True(t, s.keeper.IsSkipHeight(11))
|
|
|
|
t.Log("case where array doesn't contain the element")
|
|
require.False(t, s.keeper.IsSkipHeight(4))
|
|
}
|
|
|
|
func TestSkipUpgradeSkippingAll(t *testing.T) {
|
|
var (
|
|
skipOne int64 = 11
|
|
skipTwo int64 = 20
|
|
)
|
|
s := setupTest(t, 10, map[int64]bool{skipOne: true, skipTwo: true})
|
|
|
|
newCtx := s.ctx
|
|
|
|
err := s.keeper.ScheduleUpgrade(s.ctx, types.Plan{Name: "test", Height: skipOne})
|
|
require.NoError(t, err)
|
|
|
|
t.Log("Verify if skip upgrade flag clears upgrade plan in both cases")
|
|
s.VerifySet(t, map[int64]bool{skipOne: true, skipTwo: true})
|
|
|
|
newCtx = newCtx.WithHeaderInfo(header.Info{Height: skipOne})
|
|
err = s.preModule.PreBlock(newCtx)
|
|
require.NoError(t, err)
|
|
|
|
t.Log("Verify a second proposal also is being cleared")
|
|
err = s.keeper.ScheduleUpgrade(s.ctx, types.Plan{Name: "test2", Height: skipTwo})
|
|
require.NoError(t, err)
|
|
|
|
newCtx = newCtx.WithHeaderInfo(header.Info{Height: skipTwo})
|
|
err = s.preModule.PreBlock(newCtx)
|
|
require.NoError(t, err)
|
|
|
|
// To ensure verification is being done only after both upgrades are cleared
|
|
t.Log("Verify if both proposals are cleared")
|
|
s.VerifyCleared(t, s.ctx)
|
|
s.VerifyNotDone(t, s.ctx, "test")
|
|
s.VerifyNotDone(t, s.ctx, "test2")
|
|
}
|
|
|
|
func TestUpgradeSkippingOne(t *testing.T) {
|
|
var (
|
|
skipOne int64 = 11
|
|
skipTwo int64 = 20
|
|
)
|
|
s := setupTest(t, 10, map[int64]bool{skipOne: true})
|
|
|
|
newCtx := s.ctx
|
|
|
|
err := s.keeper.ScheduleUpgrade(s.ctx, types.Plan{Name: "test", Height: skipOne})
|
|
require.NoError(t, err)
|
|
|
|
t.Log("Verify if skip upgrade flag clears upgrade plan in one case and does upgrade on another")
|
|
s.VerifySet(t, map[int64]bool{skipOne: true})
|
|
|
|
// Setting block height of proposal test
|
|
newCtx = newCtx.WithHeaderInfo(header.Info{Height: skipOne})
|
|
err = s.preModule.PreBlock(newCtx)
|
|
require.NoError(t, err)
|
|
|
|
t.Log("Verify the second proposal is not skipped")
|
|
err = s.keeper.ScheduleUpgrade(s.ctx, types.Plan{Name: "test2", Height: skipTwo})
|
|
require.NoError(t, err)
|
|
// Setting block height of proposal test2
|
|
newCtx = newCtx.WithHeaderInfo(header.Info{Height: skipTwo})
|
|
s.VerifyDoUpgradeWithCtx(t, newCtx, "test2")
|
|
|
|
t.Log("Verify first proposal is cleared and second is done")
|
|
s.VerifyNotDone(t, s.ctx, "test")
|
|
s.VerifyDone(t, s.ctx, "test2")
|
|
}
|
|
|
|
func TestUpgradeSkippingOnlyTwo(t *testing.T) {
|
|
var (
|
|
skipOne int64 = 11
|
|
skipTwo int64 = 20
|
|
skipThree int64 = 25
|
|
)
|
|
s := setupTest(t, 10, map[int64]bool{skipOne: true, skipTwo: true})
|
|
|
|
newCtx := s.ctx
|
|
|
|
err := s.keeper.ScheduleUpgrade(s.ctx, types.Plan{Name: "test", Height: skipOne})
|
|
require.NoError(t, err)
|
|
|
|
t.Log("Verify if skip upgrade flag clears upgrade plan in both cases and does third upgrade")
|
|
s.VerifySet(t, map[int64]bool{skipOne: true, skipTwo: true})
|
|
|
|
// Setting block height of proposal test
|
|
newCtx = newCtx.WithHeaderInfo(header.Info{Height: skipOne})
|
|
err = s.preModule.PreBlock(newCtx)
|
|
require.NoError(t, err)
|
|
|
|
// A new proposal with height in skipUpgradeHeights
|
|
err = s.keeper.ScheduleUpgrade(s.ctx, types.Plan{Name: "test2", Height: skipTwo})
|
|
require.NoError(t, err)
|
|
// Setting block height of proposal test2
|
|
newCtx = newCtx.WithHeaderInfo(header.Info{Height: skipTwo})
|
|
err = s.preModule.PreBlock(newCtx)
|
|
require.NoError(t, err)
|
|
|
|
t.Log("Verify a new proposal is not skipped")
|
|
err = s.keeper.ScheduleUpgrade(s.ctx, types.Plan{Name: "test3", Height: skipThree})
|
|
require.NoError(t, err)
|
|
newCtx = newCtx.WithHeaderInfo(header.Info{Height: skipThree})
|
|
s.VerifyDoUpgradeWithCtx(t, newCtx, "test3")
|
|
|
|
t.Log("Verify two proposals are cleared and third is done")
|
|
s.VerifyNotDone(t, s.ctx, "test")
|
|
s.VerifyNotDone(t, s.ctx, "test2")
|
|
s.VerifyDone(t, s.ctx, "test3")
|
|
}
|
|
|
|
func TestUpgradeWithoutSkip(t *testing.T) {
|
|
s := setupTest(t, 10, map[int64]bool{})
|
|
newCtx := s.ctx.WithHeaderInfo(header.Info{Height: s.ctx.HeaderInfo().Height + 1, Time: time.Now()})
|
|
err := s.keeper.ScheduleUpgrade(s.ctx, types.Plan{Name: "test", Height: s.ctx.HeaderInfo().Height + 1})
|
|
require.NoError(t, err)
|
|
t.Log("Verify if upgrade happens without skip upgrade")
|
|
err = s.preModule.PreBlock(newCtx)
|
|
require.ErrorContains(t, err, "UPGRADE \"test\" NEEDED at height:")
|
|
|
|
s.VerifyDoUpgrade(t)
|
|
s.VerifyDone(t, s.ctx, "test")
|
|
}
|
|
|
|
func TestDumpUpgradeInfoToFile(t *testing.T) {
|
|
s := setupTest(t, 10, map[int64]bool{})
|
|
require := require.New(t)
|
|
|
|
// require no error when the upgrade info file does not exist
|
|
_, err := s.keeper.ReadUpgradeInfoFromDisk()
|
|
require.NoError(err)
|
|
|
|
planHeight := s.ctx.HeaderInfo().Height + 1
|
|
plan := types.Plan{
|
|
Name: "test",
|
|
Height: 0, // this should be overwritten by DumpUpgradeInfoToFile
|
|
}
|
|
t.Log("verify if upgrade height is dumped to file")
|
|
err = s.keeper.DumpUpgradeInfoToDisk(planHeight, plan)
|
|
require.Nil(err)
|
|
|
|
upgradeInfo, err := s.keeper.ReadUpgradeInfoFromDisk()
|
|
require.NoError(err)
|
|
|
|
t.Log("Verify upgrade height from file matches ")
|
|
require.Equal(upgradeInfo.Height, planHeight)
|
|
require.Equal(upgradeInfo.Name, plan.Name)
|
|
|
|
// clear the test file
|
|
upgradeInfoFilePath, err := s.keeper.GetUpgradeInfoPath()
|
|
require.Nil(err)
|
|
err = os.Remove(upgradeInfoFilePath)
|
|
require.Nil(err)
|
|
}
|
|
|
|
// TODO: add testcase to for `no upgrade handler is present for last applied upgrade`.
|
|
func TestBinaryVersion(t *testing.T) {
|
|
var skipHeight int64 = 15
|
|
s := setupTest(t, 10, map[int64]bool{skipHeight: true})
|
|
|
|
testCases := []struct {
|
|
name string
|
|
preRun func() sdk.Context
|
|
expectError bool
|
|
}{
|
|
{
|
|
"test not panic: no scheduled upgrade or applied upgrade is present",
|
|
func() sdk.Context {
|
|
return s.ctx
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"test not panic: upgrade handler is present for last applied upgrade",
|
|
func() sdk.Context {
|
|
s.keeper.SetUpgradeHandler("test0", func(_ context.Context, _ types.Plan, vm appmodule.VersionMap) (appmodule.VersionMap, error) {
|
|
return vm, nil
|
|
})
|
|
|
|
err := s.keeper.ScheduleUpgrade(s.ctx, types.Plan{Name: "test0", Height: s.ctx.HeaderInfo().Height + 2})
|
|
require.NoError(t, err)
|
|
|
|
newCtx := s.ctx.WithHeaderInfo(header.Info{Height: 12})
|
|
err = s.keeper.ApplyUpgrade(newCtx, types.Plan{
|
|
Name: "test0",
|
|
Height: 12,
|
|
})
|
|
require.NoError(t, err)
|
|
return newCtx
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"test panic: upgrade needed",
|
|
func() sdk.Context {
|
|
err := s.keeper.ScheduleUpgrade(s.ctx, types.Plan{Name: "test2", Height: 13})
|
|
require.NoError(t, err)
|
|
|
|
newCtx := s.ctx.WithHeaderInfo(header.Info{Height: 13})
|
|
return newCtx
|
|
},
|
|
true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
ctx := tc.preRun()
|
|
err := s.preModule.PreBlock(ctx)
|
|
if tc.expectError {
|
|
require.Error(t, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDowngradeVerification(t *testing.T) {
|
|
s := setupTest(t, 10, map[int64]bool{})
|
|
|
|
// submit a plan.
|
|
planName := "downgrade"
|
|
err := s.keeper.ScheduleUpgrade(s.ctx, types.Plan{Name: planName, Height: s.ctx.HeaderInfo().Height + 1})
|
|
require.NoError(t, err)
|
|
s.ctx = s.ctx.WithHeaderInfo(header.Info{Height: s.ctx.HeaderInfo().Height + 1})
|
|
|
|
// set the handler.
|
|
s.keeper.SetUpgradeHandler(planName, func(_ context.Context, _ types.Plan, vm appmodule.VersionMap) (appmodule.VersionMap, error) {
|
|
return vm, nil
|
|
})
|
|
|
|
// successful upgrade.
|
|
err = s.preModule.PreBlock(s.ctx)
|
|
require.NoError(t, err)
|
|
s.ctx = s.ctx.WithHeaderInfo(header.Info{Height: s.ctx.HeaderInfo().Height + 1})
|
|
|
|
testCases := map[string]struct {
|
|
preRun func(*keeper.Keeper, sdk.Context, string)
|
|
expectError bool
|
|
}{
|
|
"valid binary": {
|
|
preRun: func(k *keeper.Keeper, ctx sdk.Context, name string) {
|
|
k.SetUpgradeHandler(planName, func(ctx context.Context, plan types.Plan, vm appmodule.VersionMap) (appmodule.VersionMap, error) {
|
|
return vm, nil
|
|
})
|
|
},
|
|
},
|
|
"downgrade with an active plan": {
|
|
preRun: func(k *keeper.Keeper, ctx sdk.Context, name string) {
|
|
err := k.ScheduleUpgrade(ctx, types.Plan{Name: "another" + planName, Height: ctx.HeaderInfo().Height + 1})
|
|
require.NoError(t, err, name)
|
|
},
|
|
expectError: true,
|
|
},
|
|
"downgrade without any active plan": {
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for name, tc := range testCases {
|
|
ctx, _ := s.ctx.CacheContext()
|
|
|
|
authority, err := addresscodec.NewBech32Codec("cosmos").BytesToString(authtypes.NewModuleAddress(govModuleName))
|
|
require.NoError(t, err)
|
|
|
|
ctrl := gomock.NewController(t)
|
|
// downgrade. now keeper does not have the handler.
|
|
ck := upgradetestutil.NewMockConsensusKeeper(ctrl)
|
|
ck.EXPECT().AppVersion(gomock.Any()).Return(uint64(0), nil).AnyTimes()
|
|
k := keeper.NewKeeper(s.env, map[int64]bool{}, s.encCfg.Codec, t.TempDir(), nil, authority, ck)
|
|
m := upgrade.NewAppModule(k)
|
|
|
|
// assertions
|
|
lastAppliedPlan, _, err := k.GetLastCompletedUpgrade(ctx)
|
|
require.NoError(t, err)
|
|
require.Equal(t, planName, lastAppliedPlan)
|
|
require.False(t, k.HasHandler(planName))
|
|
require.False(t, k.DowngradeVerified())
|
|
_, err = k.GetUpgradePlan(ctx)
|
|
require.ErrorIs(t, err, types.ErrNoUpgradePlanFound)
|
|
|
|
if tc.preRun != nil {
|
|
tc.preRun(k, ctx, name)
|
|
}
|
|
|
|
err = m.PreBlock(ctx)
|
|
if tc.expectError {
|
|
require.Error(t, err, name)
|
|
} else {
|
|
require.NoError(t, err, name)
|
|
}
|
|
}
|
|
}
|