Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: add protection against accidental downgrades #10407

Merged
merged 32 commits into from
Jan 25, 2022
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
0f65ae5
add handler check in BeginBlock
aleem1314 Oct 19, 2021
7011976
review changes
aleem1314 Nov 3, 2021
9d8e964
add tests
aleem1314 Nov 3, 2021
6f9780e
Merge branch 'master' into aleem/10318-binary-downgrades
aleem1314 Nov 3, 2021
00aa578
review changes
aleem1314 Nov 3, 2021
bbb6259
Merge branch 'aleem/10318-binary-downgrades' of https://github.com/co…
aleem1314 Nov 3, 2021
7f51fac
review changes
aleem1314 Nov 3, 2021
b023d1a
review changes
aleem1314 Nov 9, 2021
ea8b276
review changes
aleem1314 Nov 9, 2021
0bce20c
Merge branch 'master' into aleem/10318-binary-downgrades
aleem1314 Nov 9, 2021
cac3107
Merge branch 'master' into aleem/10318-binary-downgrades
aleem1314 Nov 10, 2021
512a73c
chore: cleanup
aleem1314 Nov 10, 2021
ad8823b
Merge branch 'master' into aleem/10318-binary-downgrades
aleem1314 Nov 10, 2021
23ccc63
chore: add more tests
aleem1314 Nov 11, 2021
cc9fa0a
Merge branch 'aleem/10318-binary-downgrades' of https://github.com/co…
aleem1314 Nov 11, 2021
cf1251a
Merge branch 'master' into aleem/10318-binary-downgrades
aleem1314 Nov 11, 2021
1e8e047
chore: address review changes
aleem1314 Nov 12, 2021
670e2d6
Merge branch 'aleem/10318-binary-downgrades' of https://github.com/co…
aleem1314 Nov 12, 2021
ecd03d3
chore: cleanup
aleem1314 Nov 12, 2021
16fdc29
Merge branch 'master' of https://github.com/cosmos/cosmos-sdk into al…
aleem1314 Nov 12, 2021
1f7cc72
chore: refactor tests to table tests
aleem1314 Nov 17, 2021
665a108
Merge branch 'master' into aleem/10318-binary-downgrades
aleem1314 Nov 17, 2021
9914bfe
chore: add changelog
aleem1314 Nov 17, 2021
5a2f4ae
chore: fix test
aleem1314 Nov 17, 2021
51d7d21
chore: review changes
aleem1314 Nov 19, 2021
3cb9b82
Merge branch 'master' into aleem/10318-binary-downgrades
aleem1314 Nov 24, 2021
42c72a9
Merge branch 'master' into aleem/10318-binary-downgrades
aleem1314 Jan 10, 2022
97fada7
chore: review changes
aleem1314 Jan 10, 2022
6bae3a7
chore: move downgradeVerified to keeper
aleem1314 Jan 12, 2022
1032b33
Merge branch 'master' into aleem/10318-binary-downgrades
robert-zaremba Jan 19, 2022
42c6ac5
Merge branch 'master' into aleem/10318-binary-downgrades
robert-zaremba Jan 19, 2022
4229034
Merge branch 'master' into aleem/10318-binary-downgrades
aleem1314 Jan 25, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions x/upgrade/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,18 @@ import (
// skipUpgradeHeightArray is a set of block heights for which the upgrade must be skipped
func BeginBlocker(k keeper.Keeper, ctx sdk.Context, _ abci.RequestBeginBlock) {
defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyBeginBlocker)

lastAppliedPlan, _ := k.GetNextOrLastUpgrade(ctx)
// Throw an error if there is no upgrade handler is registered for last applied upgrade(meaning this is a wrong binary)
if lastAppliedPlan != "" && !k.HasHandler(lastAppliedPlan) {
panic(fmt.Sprintf("upgrade handler is missing for %s upgrade plan", lastAppliedPlan))
}

plan, found := k.GetUpgradePlan(ctx)
if !found {
return
}

logger := ctx.Logger()

// To make sure clear upgrade is executed at the same block
Expand Down
48 changes: 48 additions & 0 deletions x/upgrade/abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -410,3 +410,51 @@ func TestDumpUpgradeInfoToFile(t *testing.T) {
err = os.Remove(upgradeInfoFilePath)
require.Nil(err)
}

func TestBinaryVersion(t *testing.T) {
s := setupTest(t, 10, map[int64]bool{})

t.Log("verify not panic: upgrade handler is present for applied upgrade")
s.keeper.SetUpgradeHandler("test0", func(_ sdk.Context, _ types.Plan, vm module.VersionMap) (module.VersionMap, error) {
return vm, nil
})

err := s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "Upgrade test", Plan: types.Plan{Name: "test0", Height: s.ctx.BlockHeight() + 2}})
require.Nil(t, err)

newCtx := s.ctx.WithBlockHeight(12)
s.keeper.ApplyUpgrade(newCtx, types.Plan{
Name: "test0",
Height: 12,
})

newCtx = s.ctx.WithBlockHeight(13)
req := abci.RequestBeginBlock{Header: newCtx.BlockHeader()}
require.NotPanics(t, func() {
s.module.BeginBlock(newCtx, req)
})

t.Log("verify not panic: upgrade handler is present for scheduled upgrade")
s.keeper.SetUpgradeHandler("test1", func(_ sdk.Context, _ types.Plan, vm module.VersionMap) (module.VersionMap, error) {
return vm, nil
})

err = s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "Upgrade test", Plan: types.Plan{Name: "test1", Height: s.ctx.BlockHeight() + 2}})
require.Nil(t, err)

newCtx = s.ctx.WithBlockHeight(13)
req = abci.RequestBeginBlock{Header: newCtx.BlockHeader()}
require.NotPanics(t, func() {
s.module.BeginBlock(newCtx, req)
})

t.Log("verify test panic: upgrade handler is not present for scheduled upgrade")
err = s.handler(s.ctx, &types.SoftwareUpgradeProposal{Title: "Upgrade test", Plan: types.Plan{Name: "test2", Height: s.ctx.BlockHeight() + 2}})
require.Nil(t, err)

newCtx = s.ctx.WithBlockHeight(13)
req = abci.RequestBeginBlock{Header: newCtx.BlockHeader()}
require.Panics(t, func() {
s.module.BeginBlock(newCtx, req)
})
}
18 changes: 18 additions & 0 deletions x/upgrade/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/cosmos/cosmos-sdk/store/prefix"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/types/kv"
"github.com/cosmos/cosmos-sdk/types/module"
xp "github.com/cosmos/cosmos-sdk/x/upgrade/exported"
"github.com/cosmos/cosmos-sdk/x/upgrade/types"
Expand Down Expand Up @@ -227,6 +228,23 @@ func (k Keeper) GetUpgradedConsensusState(ctx sdk.Context, lastHeight int64) ([]
return bz, true
}

// GetNextOrLastUpgrade returns the last applied upgrade name and height.
func (k Keeper) GetNextOrLastUpgrade(ctx sdk.Context) (string, int64) {
aleem1314 marked this conversation as resolved.
Show resolved Hide resolved
iter := sdk.KVStoreReversePrefixIterator(ctx.KVStore(k.storeKey), []byte{types.DoneByte})
defer iter.Close()
if iter.Valid() {
aleem1314 marked this conversation as resolved.
Show resolved Hide resolved
return parseDoneKey(iter.Key()), int64(binary.BigEndian.Uint64(iter.Value()))
}

return "", 0
}

// parseDoneKey - split upgrade name from the done key
func parseDoneKey(key []byte) string {
kv.AssertKeyAtLeastLength(key, 2)
return string(key[1:])
}

// GetDoneHeight returns the height at which the given upgrade was executed
func (k Keeper) GetDoneHeight(ctx sdk.Context, name string) int64 {
store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte{types.DoneByte})
Expand Down
39 changes: 39 additions & 0 deletions x/upgrade/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,45 @@ func (s *KeeperTestSuite) TestMigrations() {
s.Require().Equal(vmBefore["bank"]+1, vm["bank"])
}

func (s *KeeperTestSuite) TestLastCompletedUpgrade() {
keeper := s.app.UpgradeKeeper
require := s.Require()

s.T().Log("verify empty name if applied upgrades are empty")
name, height := keeper.GetNextOrLastUpgrade(s.ctx)
require.Equal("", name)
require.Equal(int64(0), height)

keeper.SetUpgradeHandler("test0", func(_ sdk.Context, _ types.Plan, vm module.VersionMap) (module.VersionMap, error) {
return vm, nil
})

keeper.ApplyUpgrade(s.ctx, types.Plan{
Name: "test0",
Height: 10,
})

s.T().Log("verify valid upgrade name and height")
name, height = keeper.GetNextOrLastUpgrade(s.ctx)
require.Equal("test0", name)
require.Equal(int64(10), height)

keeper.SetUpgradeHandler("test1", func(_ sdk.Context, _ types.Plan, vm module.VersionMap) (module.VersionMap, error) {
return vm, nil
})

newCtx := s.ctx.WithBlockHeight(15)
keeper.ApplyUpgrade(newCtx, types.Plan{
Name: "test1",
Height: 15,
})

s.T().Log("verify valid upgrade name and height with multiple upgrades")
name, height = keeper.GetNextOrLastUpgrade(newCtx)
require.Equal("test1", name)
require.Equal(int64(15), height)
}

func TestKeeperTestSuite(t *testing.T) {
suite.Run(t, new(KeeperTestSuite))
}