diff --git a/CHANGELOG.md b/CHANGELOG.md index 080fd112a50d..c1a25c685a7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,10 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## [Unreleased] +### Features + +* (x/upgrade) [\#11551](https://github.com/cosmos/cosmos-sdk/pull/11551) Update `ScheduleUpgrade` for chains to schedule an automated upgrade on `BeginBlock` without having to go though governance. + ## [v0.45.2](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.45.2) - 2022-04-05 ### Features diff --git a/x/upgrade/abci_test.go b/x/upgrade/abci_test.go index 731de6af8a51..b38b6e21d425 100644 --- a/x/upgrade/abci_test.go +++ b/x/upgrade/abci_test.go @@ -64,14 +64,14 @@ func TestRequireName(t *testing.T) { s := setupTest(10, map[int64]bool{}) err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{}}) - require.NotNil(t, err) + require.Error(t, err) require.True(t, errors.Is(sdkerrors.ErrInvalidRequest, err), err) } func TestRequireFutureBlock(t *testing.T) { s := setupTest(10, map[int64]bool{}) - err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "test", Height: s.ctx.BlockHeight()}}) - require.NotNil(t, err) + err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "test", Height: s.ctx.BlockHeight() - 1}}) + require.Error(t, err) require.True(t, errors.Is(sdkerrors.ErrInvalidRequest, err), err) } @@ -79,7 +79,7 @@ func TestDoHeightUpgrade(t *testing.T) { s := setupTest(10, map[int64]bool{}) t.Log("Verify can schedule an upgrade") err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "test", Height: s.ctx.BlockHeight() + 1}}) - require.Nil(t, err) + require.NoError(t, err) VerifyDoUpgrade(t) } @@ -88,9 +88,9 @@ func TestCanOverwriteScheduleUpgrade(t *testing.T) { s := setupTest(10, map[int64]bool{}) t.Log("Can overwrite plan") err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "bad_test", Height: s.ctx.BlockHeight() + 10}}) - require.Nil(t, err) + require.NoError(t, err) err = s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "test", Height: s.ctx.BlockHeight() + 1}}) - require.Nil(t, err) + require.NoError(t, err) VerifyDoUpgrade(t) } @@ -137,7 +137,7 @@ func TestHaltIfTooNew(t *testing.T) { s := setupTest(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(ctx sdk.Context, plan types.Plan, vm module.VersionMap) (module.VersionMap, error) { + s.keeper.SetUpgradeHandler("future", func(_ sdk.Context, _ types.Plan, vm module.VersionMap) (module.VersionMap, error) { called++ return vm, nil }) @@ -180,10 +180,10 @@ func TestCanClear(t *testing.T) { s := setupTest(10, map[int64]bool{}) t.Log("Verify upgrade is scheduled") err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "test", Height: s.ctx.BlockHeight() + 100}}) - require.Nil(t, err) + require.NoError(t, err) err = s.handler(s.ctx, &types.CancelSoftwareUpgradeProposal{Title: "cancel"}) - require.Nil(t, err) + require.NoError(t, err) VerifyCleared(t, s.ctx) } @@ -192,11 +192,11 @@ func TestCantApplySameUpgradeTwice(t *testing.T) { s := setupTest(10, map[int64]bool{}) height := s.ctx.BlockHeader().Height + 1 err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "test", Height: height}}) - require.Nil(t, err) + require.NoError(t, err) VerifyDoUpgrade(t) t.Log("Verify an executed upgrade \"test\" can't be rescheduled") err = s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "test", Height: height}}) - require.NotNil(t, err) + require.Error(t, err) require.True(t, errors.Is(sdkerrors.ErrInvalidRequest, err), err) } @@ -242,9 +242,7 @@ func VerifySet(t *testing.T, skipUpgradeHeights map[int64]bool) { } func TestContains(t *testing.T) { - var ( - skipOne int64 = 11 - ) + var skipOne int64 = 11 s := setupTest(10, map[int64]bool{skipOne: true}) VerifySet(t, map[int64]bool{skipOne: true}) @@ -303,7 +301,7 @@ func TestUpgradeSkippingOne(t *testing.T) { req := abci.RequestBeginBlock{Header: newCtx.BlockHeader()} err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "test", Height: skipOne}}) - require.Nil(t, err) + require.NoError(t, err) t.Log("Verify if skip upgrade flag clears upgrade plan in one case and does upgrade on another") VerifySet(t, map[int64]bool{skipOne: true}) @@ -316,7 +314,7 @@ func TestUpgradeSkippingOne(t *testing.T) { t.Log("Verify the second proposal is not skipped") err = s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop2", Plan: types.Plan{Name: "test2", Height: skipTwo}}) - require.Nil(t, err) + require.NoError(t, err) // Setting block height of proposal test2 newCtx = newCtx.WithBlockHeight(skipTwo) VerifyDoUpgradeWithCtx(t, newCtx, "test2") @@ -338,7 +336,7 @@ func TestUpgradeSkippingOnlyTwo(t *testing.T) { req := abci.RequestBeginBlock{Header: newCtx.BlockHeader()} err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "test", Height: skipOne}}) - require.Nil(t, err) + require.NoError(t, err) t.Log("Verify if skip upgrade flag clears upgrade plan in both cases and does third upgrade") VerifySet(t, map[int64]bool{skipOne: true, skipTwo: true}) @@ -351,7 +349,7 @@ func TestUpgradeSkippingOnlyTwo(t *testing.T) { // A new proposal with height in skipUpgradeHeights err = s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop2", Plan: types.Plan{Name: "test2", Height: skipTwo}}) - require.Nil(t, err) + require.NoError(t, err) // Setting block height of proposal test2 newCtx = newCtx.WithBlockHeight(skipTwo) require.NotPanics(t, func() { @@ -360,7 +358,7 @@ func TestUpgradeSkippingOnlyTwo(t *testing.T) { t.Log("Verify a new proposal is not skipped") err = s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop3", Plan: types.Plan{Name: "test3", Height: skipThree}}) - require.Nil(t, err) + require.NoError(t, err) newCtx = newCtx.WithBlockHeight(skipThree) VerifyDoUpgradeWithCtx(t, newCtx, "test3") @@ -375,7 +373,7 @@ func TestUpgradeWithoutSkip(t *testing.T) { newCtx := s.ctx.WithBlockHeight(s.ctx.BlockHeight() + 1).WithBlockTime(time.Now()) req := abci.RequestBeginBlock{Header: newCtx.BlockHeader()} err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "prop", Plan: types.Plan{Name: "test", Height: s.ctx.BlockHeight() + 1}}) - require.Nil(t, err) + require.NoError(t, err) t.Log("Verify if upgrade happens without skip upgrade") require.Panics(t, func() { s.module.BeginBlock(newCtx, req) @@ -438,7 +436,7 @@ func TestBinaryVersion(t *testing.T) { }) err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "Upgrade test", Plan: types.Plan{Name: "test0", Height: s.ctx.BlockHeight() + 2}}) - require.Nil(t, err) + require.NoError(t, err) newCtx := s.ctx.WithBlockHeight(12) s.keeper.ApplyUpgrade(newCtx, types.Plan{ @@ -455,7 +453,7 @@ func TestBinaryVersion(t *testing.T) { "test panic: upgrade needed", func() (sdk.Context, abci.RequestBeginBlock) { err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "Upgrade test", Plan: types.Plan{Name: "test2", Height: 13}}) - require.Nil(t, err) + require.NoError(t, err) newCtx := s.ctx.WithBlockHeight(13) req := abci.RequestBeginBlock{Header: newCtx.BlockHeader()} diff --git a/x/upgrade/keeper/keeper.go b/x/upgrade/keeper/keeper.go index 7dee3efd56c9..4d2c834991d7 100644 --- a/x/upgrade/keeper/keeper.go +++ b/x/upgrade/keeper/keeper.go @@ -171,7 +171,9 @@ func (k Keeper) ScheduleUpgrade(ctx sdk.Context, plan types.Plan) error { return err } - if plan.Height <= ctx.BlockHeight() { + // NOTE: allow for the possibility of chains to schedule upgrades in begin block of the same block + // as a strategy for emergency hard fork recoveries + if plan.Height < ctx.BlockHeight() { return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "upgrade cannot be scheduled in the past") } @@ -375,7 +377,7 @@ func (k Keeper) DumpUpgradeInfoWithInfoToDisk(height int64, name string, info st return err } - return ioutil.WriteFile(upgradeInfoFilePath, bz, 0600) + return os.WriteFile(upgradeInfoFilePath, bz, 0o600) } // GetUpgradeInfoPath returns the upgrade info file path