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

Regen network/multistore upgrades #5500

Merged
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
3815c2c
Remove multistore upgrades from baseapp
anilcse Dec 27, 2019
2b09687
Add upgrade storeloaders in x/upgrade
anilcse Dec 27, 2019
78819e2
Write upgrade height before the old binary panics
anilcse Dec 27, 2019
d7bd14e
Add multistore upgrade alias
anilcse Dec 27, 2019
0ce25e5
Fix gofmt
anilcse Dec 27, 2019
237454c
Modified storeLoader to check height
Jan 4, 2020
ccefbd6
Rebase with upstream/master
anilcse Jan 4, 2020
c9a01e0
Add tests for dumping upgrade-info to filesystem
anilcse Jan 7, 2020
7f0dd8b
Fix storeloader tests
anilcse Jan 7, 2020
faf25f1
Fix imports
anilcse Jan 8, 2020
62ff38f
Merge branch 'master' of github.com:cosmos/cosmos-sdk into regen-netw…
anilcse Jan 9, 2020
6b6456b
Merge branch 'master' of github.com:cosmos/cosmos-sdk into regen-netw…
anilcse Jan 10, 2020
3a71d54
Fix imports
anilcse Jan 10, 2020
1971c7e
Added home path to keeper
Jan 10, 2020
82daafb
Performed code cleanup
sahith-narahari Jan 11, 2020
a82fcb5
Resolved conflicts
sahith-narahari Jan 11, 2020
5625f23
Merge branch 'regen-network/multistore-upgrades' of github.com:regen-…
sahith-narahari Jan 12, 2020
28f7bca
Added home path to keeper
sahith-narahari Jan 13, 2020
f8841b5
Removed store loader from disk
Jan 30, 2020
3cc4be0
Merge branch 'master' of github.com:cosmos/cosmos-sdk into regen-netw…
anilcse Jan 31, 2020
bcdea87
revert unexpected change
anilcse Jan 31, 2020
ccdc853
Refactor upgrade storeloader declarations
anilcse Jan 31, 2020
9546f73
code cleanup
anilcse Jan 31, 2020
66a90b2
Fix naming issues
anilcse Jan 31, 2020
aaa7d2d
Fix testscripts
anilcse Feb 1, 2020
0eeeb6f
Merge branch 'anil/review-fixes-multistore-upgrades' of github.com:re…
anilcse Feb 3, 2020
27bd739
Merge pull request #18 from regen-network/sahith/remove-deprecated-mu…
anilcse Feb 3, 2020
ec78a10
Remove demo code
anilcse Feb 3, 2020
ee97a70
Ensure no-error while writing upgrade-info to disk
anilcse Feb 3, 2020
569a238
Merge branch 'master' of github.com:cosmos/cosmos-sdk into regen-netw…
anilcse Feb 3, 2020
f4e2357
Fix godoc for UpgradeInfo
anilcse Feb 3, 2020
69b346f
Merge branch 'master' of github.com:cosmos/cosmos-sdk into regen-netw…
anilcse Feb 4, 2020
f55deac
Fix store upgrade test scripts
anilcse Feb 4, 2020
8a352c4
Fix godoc
anilcse Feb 4, 2020
c33a01e
Merge branch 'master' of github.com:cosmos/cosmos-sdk into regen-netw…
anilcse Feb 4, 2020
1e048d2
Refactor and update store migrations usage doc
anilcse Feb 5, 2020
5e3d8e8
Update godoc
anilcse Feb 5, 2020
a3b2c3e
Fix godoc
anilcse Feb 5, 2020
19d1268
Update upgrade spec with StoreLoader
anilcse Feb 5, 2020
c0a5bf0
Update upgrade spec doc
anilcse Feb 5, 2020
a20eb9a
Merge branch 'master' into regen-network/multistore-upgrades
anilcse Feb 21, 2020
daa8f4a
Fix review updates
anilcse Feb 21, 2020
05d35d8
Fix fmt
anilcse Feb 21, 2020
5500774
Merge pull request #28 from regen-network/anil/multistore-upgrades
anilcse Feb 26, 2020
9917cfd
Update upgrade concepts doc
anilcse Feb 26, 2020
c268fec
Merge branch 'regen-network/multistore-upgrades' of github.com:regen-…
anilcse Feb 26, 2020
2d0bb5a
Update simapp constructor to accept homePath
anilcse Feb 27, 2020
c42582a
Merge branch 'master' of github.com:cosmos/cosmos-sdk into regen-netw…
anilcse Feb 27, 2020
175190c
Merge branch 'master' into regen-network/multistore-upgrades
anilcse Feb 27, 2020
0959685
Update x/upgrade/spec/01_concepts.md
alexanderbez Feb 27, 2020
6120306
Merge branch 'master' of github.com:cosmos/cosmos-sdk into regen-netw…
anilcse Feb 27, 2020
a8a4dbf
Merge branch 'regen-network/multistore-upgrades' of github.com:regen-…
anilcse Feb 27, 2020
1e22e61
Merge branch 'master' into regen-network/multistore-upgrades
alexanderbez Feb 28, 2020
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
60 changes: 0 additions & 60 deletions baseapp/baseapp.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package baseapp

import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"reflect"
"runtime/debug"
"strings"
Expand All @@ -17,7 +14,6 @@ import (
dbm "github.com/tendermint/tm-db"

"github.com/cosmos/cosmos-sdk/store"
storetypes "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
Expand Down Expand Up @@ -226,62 +222,6 @@ func DefaultStoreLoader(ms sdk.CommitMultiStore) error {
return ms.LoadLatestVersion()
}

// StoreLoaderWithUpgrade is used to prepare baseapp with a fixed StoreLoader
// pattern. This is useful in test cases, or with custom upgrade loading logic.
func StoreLoaderWithUpgrade(upgrades *storetypes.StoreUpgrades) StoreLoader {
return func(ms sdk.CommitMultiStore) error {
return ms.LoadLatestVersionAndUpgrade(upgrades)
}
}

// UpgradeableStoreLoader can be configured by SetStoreLoader() to check for the
// existence of a given upgrade file - json encoded StoreUpgrades data.
//
// If not file is present, it will peform the default load (no upgrades to store).
//
// If the file is present, it will parse the file and execute those upgrades
// (rename or delete stores), while loading the data. It will also delete the
// upgrade file upon successful load, so that the upgrade is only applied once,
// and not re-applied on next restart
//
// This is useful for in place migrations when a store key is renamed between
// two versions of the software. (TODO: this code will move to x/upgrades
// when PR #4233 is merged, here mainly to help test the design)
func UpgradeableStoreLoader(upgradeInfoPath string) StoreLoader {
return func(ms sdk.CommitMultiStore) error {
_, err := os.Stat(upgradeInfoPath)
if os.IsNotExist(err) {
return DefaultStoreLoader(ms)
} else if err != nil {
return err
}

// there is a migration file, let's execute
data, err := ioutil.ReadFile(upgradeInfoPath)
if err != nil {
return fmt.Errorf("cannot read upgrade file %s: %v", upgradeInfoPath, err)
}

var upgrades storetypes.StoreUpgrades
err = json.Unmarshal(data, &upgrades)
if err != nil {
return fmt.Errorf("cannot parse upgrade file: %v", err)
}

err = ms.LoadLatestVersionAndUpgrade(&upgrades)
if err != nil {
return fmt.Errorf("load and upgrade database: %v", err)
}

// if we have a successful load, we delete the file
err = os.Remove(upgradeInfoPath)
if err != nil {
return fmt.Errorf("deleting upgrade file %s: %v", upgradeInfoPath, err)
}
return nil
}
}

// LoadVersion loads the BaseApp application version. It will panic if called
// more than once on a running baseapp.
func (app *BaseApp) LoadVersion(version int64, baseKey *sdk.KVStoreKey) error {
Expand Down
50 changes: 0 additions & 50 deletions baseapp/baseapp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"bytes"
"encoding/binary"
"fmt"
"io/ioutil"
"os"
"sync"
"testing"
Expand Down Expand Up @@ -137,18 +136,6 @@ func useDefaultLoader(app *BaseApp) {
app.SetStoreLoader(DefaultStoreLoader)
}

func useUpgradeLoader(upgrades *store.StoreUpgrades) func(*BaseApp) {
return func(app *BaseApp) {
app.SetStoreLoader(StoreLoaderWithUpgrade(upgrades))
}
}

func useFileUpgradeLoader(upgradeInfoPath string) func(*BaseApp) {
return func(app *BaseApp) {
app.SetStoreLoader(UpgradeableStoreLoader(upgradeInfoPath))
}
}

func initStore(t *testing.T, db dbm.DB, storeKey string, k, v []byte) {
rs := rootmulti.NewStore(db)
rs.SetPruning(store.PruneSyncable)
Expand Down Expand Up @@ -184,19 +171,6 @@ func checkStore(t *testing.T, db dbm.DB, ver int64, storeKey string, k, v []byte
// Test that we can make commits and then reload old versions.
// Test that LoadLatestVersion actually does.
func TestSetLoader(t *testing.T) {
// write a renamer to a file
f, err := ioutil.TempFile("", "upgrade-*.json")
require.NoError(t, err)
data := []byte(`{"renamed":[{"old_key": "bnk", "new_key": "banker"}]}`)
_, err = f.Write(data)
require.NoError(t, err)
configName := f.Name()
require.NoError(t, f.Close())

// make sure it exists before running everything
_, err = os.Stat(configName)
require.NoError(t, err)

cases := map[string]struct {
setLoader func(*BaseApp)
origStoreKey string
Expand All @@ -211,26 +185,6 @@ func TestSetLoader(t *testing.T) {
origStoreKey: "foo",
loadStoreKey: "foo",
},
"rename with inline opts": {
setLoader: useUpgradeLoader(&store.StoreUpgrades{
Renamed: []store.StoreRename{{
OldKey: "foo",
NewKey: "bar",
}},
}),
origStoreKey: "foo",
loadStoreKey: "bar",
},
"file loader with missing file": {
setLoader: useFileUpgradeLoader(configName + "randomchars"),
origStoreKey: "bnk",
loadStoreKey: "bnk",
},
"file loader with existing file": {
setLoader: useFileUpgradeLoader(configName),
origStoreKey: "bnk",
loadStoreKey: "banker",
},
}

k := []byte("key")
Expand Down Expand Up @@ -265,10 +219,6 @@ func TestSetLoader(t *testing.T) {
checkStore(t, db, 2, tc.loadStoreKey, []byte("foo"), nil)
})
}

// ensure config file was deleted
_, err = os.Stat(configName)
require.True(t, os.IsNotExist(err))
}

func TestAppVersionSetterGetter(t *testing.T) {
Expand Down
6 changes: 6 additions & 0 deletions store/types/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ type StoreUpgrades struct {
Deleted []string `json:"deleted"`
}

// UpgradeInfo defines height and StoreUpgrades to be used for multistore upgrades
type UpgradeInfo struct {
Height int64 `json:"height"`
StoreUpgrades StoreUpgrades `json:"store_upgrades"`
}

// StoreRename defines a name change of a sub-store.
// All data previously under a PrefixStore with OldKey will be copied
// to a PrefixStore with NewKey, then deleted from OldKey store.
Expand Down
6 changes: 6 additions & 0 deletions x/upgrade/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ func BeginBlocker(k Keeper, ctx sdk.Context, _ abci.RequestBeginBlock) {
upgradeMsg := fmt.Sprintf("UPGRADE \"%s\" NEEDED at %s: %s", plan.Name, plan.DueAt(), plan.Info)
// We don't have an upgrade handler for this upgrade name, meaning this software is out of date so shutdown
ctx.Logger().Error(upgradeMsg)

// Write upgrade height info to filesystem
// So multistore upgrades can make use of this height to determine
// to continue or skip the multistore upgrades on launching new binary
k.DumpUpgradeInfoToFile(ctx.BlockHeight())

panic(upgradeMsg)
}
// We have an upgrade handler for this upgrade name, so apply the upgrade
Expand Down
49 changes: 49 additions & 0 deletions x/upgrade/abci_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
package upgrade_test

import (
"encoding/json"
"errors"
"github.com/cosmos/cosmos-sdk/client/flags"
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
store "github.com/cosmos/cosmos-sdk/store/types"
"github.com/cosmos/cosmos-sdk/tests"
"github.com/spf13/viper"
"io/ioutil"
"os"
"path/filepath"
"testing"
"time"

Expand Down Expand Up @@ -393,3 +401,44 @@ func TestUpgradeWithoutSkip(t *testing.T) {
VerifyDoUpgrade(t)
VerifyDone(t, s.ctx, "test")
}

func TestDumpUpgradeInfoToFile(t *testing.T) {
s := setupTest(10, map[int64]bool{})
newCtx := s.ctx.WithBlockHeight(s.ctx.BlockHeight() + 1).WithBlockTime(time.Now())

// set a temporary home dir
homeDir, cleanUp := tests.NewTestCaseDir(t)
defer cleanUp()
// TODO cleanup viper
viper.Set(flags.FlagHome, homeDir)

req := abci.RequestBeginBlock{Header: newCtx.BlockHeader()}
planHeight := s.ctx.BlockHeight() + 1
err := s.handler(s.ctx, upgrade.SoftwareUpgradeProposal{Title: "prop", Plan: upgrade.Plan{Name: "test", Height: planHeight}})
require.Nil(t, err)

t.Log("verify if binary panics when there exists a upgrade plan")
require.Panics(t, func() {
s.module.BeginBlock(newCtx, req)
})

// TODO cleanup viper
home := viper.GetString(flags.FlagHome)
upgradeInfoFilePath := filepath.Join(home, "upgrade-info.json")

_, err = os.Stat(upgradeInfoFilePath)

t.Log("verify if upgrade height is written to filesystem when binary panics")
require.Nil(t, err)

var upgradeInfo store.UpgradeInfo
info, err := ioutil.ReadFile(upgradeInfoFilePath)
require.Nil(t, err)

err = json.Unmarshal(info, &upgradeInfo)
require.Nil(t, err)
require.Equal(t, planHeight, upgradeInfo.Height)

VerifyDoUpgrade(t)
VerifyDone(t, s.ctx, "test")
}
2 changes: 2 additions & 0 deletions x/upgrade/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ var (
NewQueryAppliedParams = types.NewQueryAppliedParams
NewKeeper = keeper.NewKeeper
NewQuerier = keeper.NewQuerier
UpgradeableStoreLoader = types.UpgradeableStoreLoader
StoreLoaderWithUpgrade = types.StoreLoaderWithUpgrade
)

type (
Expand Down
37 changes: 37 additions & 0 deletions x/upgrade/internal/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@ package keeper

import (
"encoding/binary"
"encoding/json"
"fmt"
"path/filepath"

"github.com/spf13/viper"

"io/ioutil"
"os"

"github.com/tendermint/tendermint/libs/log"

"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/store/prefix"
store "github.com/cosmos/cosmos-sdk/store/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/cosmos/cosmos-sdk/x/upgrade/internal/types"
Expand Down Expand Up @@ -130,3 +139,31 @@ func (k Keeper) ApplyUpgrade(ctx sdk.Context, plan types.Plan) {
func (k Keeper) IsSkipHeight(height int64) bool {
return k.skipUpgradeHeights[height]
}

// WriteToFile adds plan height to upgrade-info.json
func (k Keeper) DumpUpgradeInfoToFile(height int64) {
// TODO cleanup viper
home := viper.GetString(flags.FlagHome)
upgradeInfoFilePath := filepath.Join(home, "upgrade-info.json")
_, err := os.Stat(upgradeInfoFilePath)

// If the upgrade-info file is not found, create new
if os.IsNotExist(err) {
_, err := os.Create(upgradeInfoFilePath)

if err != nil {
panic(fmt.Errorf("error while creating upgrade-info file: %s", err))
}
}

var upgradeInfo store.UpgradeInfo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So UpgradeInfo includes StoreUpgrades. Where do we set those? I never see those set by searching the diff (but maybe I missed it)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea I had was for just Plan to get serialized to json. I think that's likely how it should work. The store upgrades wouldn't get serialized to disk here.

upgradeInfo.Height = height

info, err := json.Marshal(upgradeInfo)

if err != nil {
panic(fmt.Errorf("unable to write upgrade info to filesystem: %s", err.Error()))
}

err = ioutil.WriteFile(upgradeInfoFilePath, info, 0644)
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
}
Loading