diff --git a/CHANGELOG.md b/CHANGELOG.md index 30bf59c12d833..ee70a2a12afb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Improvements * (cli) [#14953](https://github.com/cosmos/cosmos-sdk/pull/14953) Enable profiling block replay during abci handshake with `--cpu-profile`. +* (store) [#14410](https://github.com/cosmos/cosmos-sdk/pull/14410) `rootmulti.Store.loadVersion` has validation to check if all the module stores' height is correct, it will error if any module store has incorrect height. ## [v0.46.9](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.46.9) - 2022-02-07 diff --git a/go.mod b/go.mod index 9330a7a227609..b0eb00e1a4430 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,7 @@ require ( github.com/spf13/viper v1.13.0 github.com/stretchr/testify v1.8.1 github.com/tendermint/go-amino v0.16.0 - github.com/tendermint/tendermint v0.34.26 + github.com/tendermint/tendermint v0.34.24 github.com/tendermint/tm-db v0.6.7 github.com/tidwall/btree v1.5.0 golang.org/x/crypto v0.5.0 @@ -167,7 +167,6 @@ replace ( // TODO Remove it: https://github.com/cosmos/cosmos-sdk/issues/10409 github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.7.0 github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 - github.com/jhump/protoreflect => github.com/jhump/protoreflect v1.9.0 // use informal system fork of tendermint github.com/tendermint/tendermint => github.com/informalsystems/tendermint v0.34.26 diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index 3b5fede4321d2..dd81d771eec5f 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -134,11 +134,7 @@ func (rs *Store) MountStoreWithDB(key types.StoreKey, typ types.StoreType, db db if _, ok := rs.keysByName[key.Name()]; ok { panic(fmt.Sprintf("store duplicate store key name %v", key)) } - rs.storesParams[key] = storeParams{ - key: key, - typ: typ, - db: db, - } + rs.storesParams[key] = newStoreParams(key, db, typ, 0) rs.keysByName[key.Name()] = key } @@ -231,8 +227,10 @@ func (rs *Store) loadVersion(ver int64, upgrades *types.StoreUpgrades) error { commitID := rs.getCommitID(infos, key.Name()) // If it has been added, set the initial version - if upgrades.IsAdded(key.Name()) { + if upgrades.IsAdded(key.Name()) || upgrades.RenamedFrom(key.Name()) != "" { storeParams.initialVersion = uint64(ver) + 1 + } else if commitID.Version != ver && storeParams.typ == types.StoreTypeIAVL { + return fmt.Errorf("version of store %s mismatch root store's version; expected %d got %d", key.Name(), ver, commitID.Version) } store, err := rs.loadCommitStoreFromParams(key, commitID, storeParams) @@ -252,8 +250,7 @@ func (rs *Store) loadVersion(ver int64, upgrades *types.StoreUpgrades) error { // handle renames specially // make an unregistered key to satisfy loadCommitStore params oldKey := types.NewKVStoreKey(oldName) - oldParams := storeParams - oldParams.key = oldKey + oldParams := newStoreParams(oldKey, storeParams.db, storeParams.typ, 0) // load from the old name oldStore, err := rs.loadCommitStoreFromParams(oldKey, rs.getCommitID(infos, oldName), oldParams) @@ -1009,6 +1006,15 @@ type storeParams struct { initialVersion uint64 } +func newStoreParams(key types.StoreKey, db dbm.DB, typ types.StoreType, initialVersion uint64) storeParams { // nolint + return storeParams{ + key: key, + db: db, + typ: typ, + initialVersion: initialVersion, + } +} + func GetLatestVersion(db dbm.DB) int64 { bz, err := db.Get([]byte(latestVersionKey)) if err != nil { diff --git a/store/rootmulti/store_test.go b/store/rootmulti/store_test.go index 26204bceb21b4..0e78e0f95bed9 100644 --- a/store/rootmulti/store_test.go +++ b/store/rootmulti/store_test.go @@ -273,6 +273,12 @@ func TestMultistoreLoadWithUpgrade(t *testing.T) { require.Equal(t, migratedID.Version, int64(2)) reload, _ := newMultiStoreWithModifiedMounts(db, pruningtypes.NewPruningOptions(pruningtypes.PruningNothing)) + // unmount store3 since store3 was deleted + unmountStore(reload, "store3") + + rs3, _ := reload.GetStoreByName("store3").(types.KVStore) + require.Nil(t, rs3) + err = reload.LoadLatestVersion() require.Nil(t, err) require.Equal(t, migratedID, reload.LastCommitID()) @@ -607,6 +613,32 @@ func TestMultiStore_PruningRestart(t *testing.T) { } } +// TestUnevenStoresHeightCheck tests if loading root store correctly errors when +// there's any module store with the wrong height +func TestUnevenStoresHeightCheck(t *testing.T) { + var db dbm.DB = dbm.NewMemDB() + store := newMultiStoreWithMounts(db, pruningtypes.NewPruningOptions(pruningtypes.PruningNothing)) + err := store.LoadLatestVersion() + require.Nil(t, err) + + // commit to increment store's height + store.Commit() + + // mount store4 to root store + store.MountStoreWithDB(types.NewKVStoreKey("store4"), types.StoreTypeIAVL, nil) + + // load the stores without upgrades + err = store.LoadLatestVersion() + require.Error(t, err) + + // now, let's load with upgrades... + upgrades := &types.StoreUpgrades{ + Added: []string{"store4"}, + } + err = store.LoadLatestVersionAndUpgrade(upgrades) + require.Nil(t, err) +} + func TestSetInitialVersion(t *testing.T) { db := dbm.NewMemDB() multi := newMultiStoreWithMounts(db, pruningtypes.NewPruningOptions(pruningtypes.PruningNothing)) @@ -868,6 +900,13 @@ func newMultiStoreWithModifiedMounts(db dbm.DB, pruningOpts pruningtypes.Pruning return store, upgrades } +func unmountStore(rootStore *Store, storeKeyName string) { + sk := rootStore.keysByName[storeKeyName] + delete(rootStore.stores, sk) + delete(rootStore.storesParams, sk) + delete(rootStore.keysByName, storeKeyName) +} + func checkStore(t *testing.T, store *Store, expect, got types.CommitID) { require.Equal(t, expect, got) require.Equal(t, expect, store.LastCommitID()) diff --git a/x/upgrade/types/storeloader_test.go b/x/upgrade/types/storeloader_test.go index 6aaefa69d198d..783c15caa346f 100644 --- a/x/upgrade/types/storeloader_test.go +++ b/x/upgrade/types/storeloader_test.go @@ -151,11 +151,6 @@ func TestSetLoader(t *testing.T) { res := app.Commit() require.NotNil(t, res.Data) - // checking the case of the store being renamed - if tc.setLoader != nil { - checkStore(t, db, upgradeHeight, tc.origStoreKey, k, nil) - } - // check db is properly updated checkStore(t, db, upgradeHeight, tc.loadStoreKey, k, v) })