diff --git a/CHANGELOG.md b/CHANGELOG.md index bd0e455be223..231d3ceda958 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -217,6 +217,7 @@ invalid or incomplete requests. * (x/genutil) [\#5938](https://github.com/cosmos/cosmos-sdk/pull/5938) Fix `InitializeNodeValidatorFiles` error handling. * (x/staking) [\#5949](https://github.com/cosmos/cosmos-sdk/pull/5949) Skip staking `HistoricalInfoKey` in simulations as headers are not exported. * (client) [\#5964](https://github.com/cosmos/cosmos-sdk/issues/5964) `--trust-node` is now false by default - for real. Users must ensure it is set to true if they don't want to enable the verifier. +* (kvstore) [\#7415](https://github.com/cosmos/cosmos-sdk/pull/7415) Allow new stores to be registered during on-chain upgrades. ### State Machine Breaking diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 760e624eb99f..233fbabc4212 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -422,7 +422,7 @@ func TestLoadVersionPruning(t *testing.T) { for _, v := range []int64{1, 2, 4} { _, err = app.cms.CacheMultiStoreWithVersion(v) - require.Error(t, err) + require.NoError(t, err) } for _, v := range []int64{3, 5, 6, 7} { diff --git a/go.mod b/go.mod index ae408db2f25d..140b1c0836d5 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ module github.com/cosmos/cosmos-sdk require ( github.com/99designs/keyring v1.1.6 + github.com/DataDog/zstd v1.4.5 // indirect github.com/armon/go-metrics v0.3.4 github.com/bgentry/speakeasy v0.1.0 github.com/btcsuite/btcd v0.21.0-beta @@ -12,11 +13,15 @@ require ( github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d github.com/cosmos/iavl v0.15.0-rc3 github.com/cosmos/ledger-cosmos-go v0.11.1 + github.com/dgraph-io/badger/v2 v2.2007.2 // indirect + github.com/dgraph-io/ristretto v0.0.3 // indirect + github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/enigmampc/btcutil v1.0.3-0.20200723161021-e2fb6adb2a25 github.com/gogo/gateway v1.1.0 github.com/gogo/protobuf v1.3.1 github.com/golang/mock v1.4.4 github.com/golang/protobuf v1.4.2 + github.com/golang/snappy v0.0.2 // indirect github.com/gorilla/handlers v1.5.1 github.com/gorilla/mux v1.8.0 github.com/grpc-ecosystem/grpc-gateway v1.15.2 @@ -43,6 +48,8 @@ require ( github.com/tendermint/tendermint v0.34.0-rc4.0.20201005135527-d7d0ffea13c6 github.com/tendermint/tm-db v0.6.2 golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a + golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 // indirect + golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f // indirect google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 google.golang.org/grpc v1.32.0 google.golang.org/protobuf v1.25.0 diff --git a/go.sum b/go.sum index 989f37edf01a..aec446a89cf5 100644 --- a/go.sum +++ b/go.sum @@ -26,6 +26,8 @@ github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1: github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -139,13 +141,19 @@ github.com/dgraph-io/badger/v2 v2.0.3 h1:inzdf6VF/NZ+tJ8RwwYMjJMvsOALTHYdozn0qSl github.com/dgraph-io/badger/v2 v2.0.3/go.mod h1:3KY8+bsP8wI0OEnQJAKpd4wIJW/Mm32yw2j/9FUVnIM= github.com/dgraph-io/badger/v2 v2.2007.1 h1:t36VcBCpo4SsmAD5M8wVv1ieVzcALyGfaJ92z4ccULM= github.com/dgraph-io/badger/v2 v2.2007.1/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= +github.com/dgraph-io/badger/v2 v2.2007.2 h1:EjjK0KqwaFMlPin1ajhP943VPENHJdEz1KLIegjaI3k= +github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/dgraph-io/ristretto v0.0.2-0.20200115201040-8f368f2f2ab3 h1:MQLRM35Pp0yAyBYksjbj1nZI/w6eyRY/mWoM1sFf4kU= github.com/dgraph-io/ristretto v0.0.2-0.20200115201040-8f368f2f2ab3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de h1:t0UHb5vdojIDUqktM6+xJAfScFBsVpXZmqC9dsgJmeA= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgraph-io/ristretto v0.0.3 h1:jh22xisGBjrEVnRZ1DVTpBVQm0Xndu8sMl0CWDzSIBI= +github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= @@ -228,6 +236,8 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw= +github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -681,6 +691,8 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc h1:zK/HqS5bZxDptfPJNq8v7vJfXtkU7r9TLIoSr1bXaP4= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200930145003-4acb6c075d10 h1:YfxMZzv3PjGonQYNUaeU2+DhAdqOxerQ30JFB6WgAXo= +golang.org/x/net v0.0.0-20200930145003-4acb6c075d10/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -729,6 +741,8 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed h1:J22ig1FUekjjkmZUM7pTKixYm8DvrYsvrBZdunYeIuQ= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= diff --git a/store/iavl/store.go b/store/iavl/store.go index 3d6bedabdbbb..cf7737d5d814 100644 --- a/store/iavl/store.go +++ b/store/iavl/store.go @@ -43,7 +43,14 @@ type Store struct { // store's version (id) from the provided DB. An error is returned if the version // fails to load. func LoadStore(db dbm.DB, id types.CommitID, lazyLoading bool) (types.CommitKVStore, error) { - tree, err := iavl.NewMutableTree(db, defaultIAVLCacheSize) + return LoadStoreWithInitialVersion(db, id, lazyLoading, 0) +} + +// LoadStore returns an IAVL Store as a CommitKVStore setting its initialVersion +// to the one given. Internally, it will load the store's version (id) from the +// provided DB. An error is returned if the version fails to load. +func LoadStoreWithInitialVersion(db dbm.DB, id types.CommitID, lazyLoading bool, initialVersion uint64) (types.CommitKVStore, error) { + tree, err := iavl.NewMutableTreeWithOpts(db, defaultIAVLCacheSize, &iavl.Options{InitialVersion: initialVersion}) if err != nil { return nil, err } @@ -78,11 +85,11 @@ func UnsafeNewStore(tree *iavl.MutableTree) *Store { // GetImmutable returns a reference to a new store backed by an immutable IAVL // tree at a specific version (height) without any pruning options. This should // be used for querying and iteration only. If the version does not exist or has -// been pruned, an error will be returned. Any mutable operations executed will -// result in a panic. +// been pruned, an empty immutable IAVL tree will be used. +// Any mutable operations executed will result in a panic. func (st *Store) GetImmutable(version int64) (*Store, error) { if !st.VersionExists(version) { - return nil, iavl.ErrVersionDoesNotExist + return &Store{tree: &immutableTree{&iavl.ImmutableTree{}}}, nil } iTree, err := st.tree.GetImmutable(version) @@ -153,7 +160,6 @@ func (st *Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.Ca // Implements types.KVStore. func (st *Store) Set(key, value []byte) { - defer telemetry.MeasureSince(time.Now(), "store", "iavl", "set") types.AssertValidKey(key) types.AssertValidValue(value) st.tree.Set(key, value) diff --git a/store/iavl/store_test.go b/store/iavl/store_test.go index 052021310f99..790830038ba9 100644 --- a/store/iavl/store_test.go +++ b/store/iavl/store_test.go @@ -50,6 +50,62 @@ func newAlohaTree(t *testing.T, db dbm.DB) (*iavl.MutableTree, types.CommitID) { return tree, types.CommitID{Version: ver, Hash: hash} } +func TestLoadStore(t *testing.T) { + db := dbm.NewMemDB() + tree, _ := newAlohaTree(t, db) + store := UnsafeNewStore(tree) + + // Create non-pruned height H + require.True(t, tree.Set([]byte("hello"), []byte("hallo"))) + hash, verH, err := tree.SaveVersion() + cIDH := types.CommitID{Version: verH, Hash: hash} + require.Nil(t, err) + + // Create pruned height Hp + require.True(t, tree.Set([]byte("hello"), []byte("hola"))) + hash, verHp, err := tree.SaveVersion() + cIDHp := types.CommitID{Version: verHp, Hash: hash} + require.Nil(t, err) + + // TODO: Prune this height + + // Create current height Hc + require.True(t, tree.Set([]byte("hello"), []byte("ciao"))) + hash, verHc, err := tree.SaveVersion() + cIDHc := types.CommitID{Version: verHc, Hash: hash} + require.Nil(t, err) + + // Querying an existing store at some previous non-pruned height H + hStore, err := store.GetImmutable(verH) + require.NoError(t, err) + require.Equal(t, string(hStore.Get([]byte("hello"))), "hallo") + + // Querying an existing store at some previous pruned height Hp + hpStore, err := store.GetImmutable(verHp) + require.NoError(t, err) + require.Equal(t, string(hpStore.Get([]byte("hello"))), "hola") + + // Querying an existing store at current height Hc + hcStore, err := store.GetImmutable(verHc) + require.NoError(t, err) + require.Equal(t, string(hcStore.Get([]byte("hello"))), "ciao") + + // Querying a new store at some previous non-pruned height H + newHStore, err := LoadStore(db, cIDH, false) + require.NoError(t, err) + require.Equal(t, string(newHStore.Get([]byte("hello"))), "hallo") + + // Querying a new store at some previous pruned height Hp + newHpStore, err := LoadStore(db, cIDHp, false) + require.NoError(t, err) + require.Equal(t, string(newHpStore.Get([]byte("hello"))), "hola") + + // Querying a new store at current height H + newHcStore, err := LoadStore(db, cIDHc, false) + require.NoError(t, err) + require.Equal(t, string(newHcStore.Get([]byte("hello"))), "ciao") +} + func TestGetImmutable(t *testing.T) { db := dbm.NewMemDB() tree, cID := newAlohaTree(t, db) @@ -61,7 +117,7 @@ func TestGetImmutable(t *testing.T) { require.Nil(t, err) _, err = store.GetImmutable(cID.Version + 1) - require.Error(t, err) + require.NoError(t, err) newStore, err := store.GetImmutable(cID.Version - 1) require.NoError(t, err) diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index bf8a473b8d0b..90633a922078 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -187,7 +187,14 @@ func (rs *Store) loadVersion(ver int64, upgrades *types.StoreUpgrades) error { var newStores = make(map[types.StoreKey]types.CommitKVStore) for key, storeParams := range rs.storesParams { - store, err := rs.loadCommitStoreFromParams(key, rs.getCommitID(infos, key.Name()), storeParams) + commitID := rs.getCommitID(infos, key.Name()) + + // If it has been added, set the initial version + if upgrades.IsAdded(key.Name()) { + storeParams.initialVersion = uint64(ver) + 1 + } + + store, err := rs.loadCommitStoreFromParams(key, commitID, storeParams) if err != nil { return errors.Wrap(err, "failed to load store") } @@ -813,7 +820,15 @@ func (rs *Store) loadCommitStoreFromParams(key types.StoreKey, id types.CommitID panic("recursive MultiStores not yet supported") case types.StoreTypeIAVL: - store, err := iavl.LoadStore(db, id, rs.lazyLoading) + var store types.CommitKVStore + var err error + + if params.initialVersion == 0 { + store, err = iavl.LoadStore(db, id, rs.lazyLoading) + } else { + store, err = iavl.LoadStoreWithInitialVersion(db, id, rs.lazyLoading, params.initialVersion) + } + if err != nil { return nil, err } @@ -868,9 +883,10 @@ func (rs *Store) buildCommitInfo(version int64) *types.CommitInfo { } type storeParams struct { - key types.StoreKey - db dbm.DB - typ types.StoreType + key types.StoreKey + db dbm.DB + typ types.StoreType + initialVersion uint64 } func getLatestVersion(db dbm.DB) int64 { diff --git a/store/rootmulti/store_test.go b/store/rootmulti/store_test.go index 7341154f2edd..efe2618779a4 100644 --- a/store/rootmulti/store_test.go +++ b/store/rootmulti/store_test.go @@ -79,9 +79,9 @@ func TestCacheMultiStoreWithVersion(t *testing.T) { cID := ms.Commit() require.Equal(t, int64(1), cID.Version) - // require failure when given an invalid or pruned version + // require no failure when given an invalid or pruned version _, err = ms.CacheMultiStoreWithVersion(cID.Version + 1) - require.Error(t, err) + require.NoError(t, err) // require a valid version can be cache-loaded cms, err := ms.CacheMultiStoreWithVersion(cID.Version) @@ -192,6 +192,9 @@ func TestMultistoreLoadWithUpgrade(t *testing.T) { require.NotNil(t, s3) s3.Set(k3, v3) + s4, _ := store.getStoreByName("store4").(types.KVStore) + require.Nil(t, s4) + // do one commit commitID := store.Commit() expectedCommitID := getExpectedCommitID(store, 1) @@ -231,6 +234,24 @@ func TestMultistoreLoadWithUpgrade(t *testing.T) { require.NotNil(t, s3) require.Nil(t, s3.Get(k3)) // data was deleted + // store4 is mounted, with empty data + s4, _ = restore.getStoreByName("store4").(types.KVStore) + require.NotNil(t, s4) + + iterator := s4.Iterator(nil, nil) + + values := 0 + for ; iterator.Valid(); iterator.Next() { + values += 1 + } + require.Zero(t, values) + + require.NoError(t, iterator.Close()) + + // write something inside store4 + k4, v4 := []byte("fourth"), []byte("created") + s4.Set(k4, v4) + // store2 is no longer mounted st2 := restore.getStoreByName("store2") require.Nil(t, st2) @@ -258,12 +279,16 @@ func TestMultistoreLoadWithUpgrade(t *testing.T) { require.NotNil(t, rl2) require.Equal(t, v2, rl2.Get(k2)) + rl4, _ := reload.getStoreByName("store4").(types.KVStore) + require.NotNil(t, rl4) + require.Equal(t, v4, rl4.Get(k4)) + // check commitInfo in storage ci, err = getCommitInfo(db, 2) require.NoError(t, err) require.Equal(t, int64(2), ci.Version) - require.Equal(t, 3, len(ci.StoreInfos), ci.StoreInfos) - checkContains(t, ci.StoreInfos, []string{"store1", "restore2", "store3"}) + require.Equal(t, 4, len(ci.StoreInfos), ci.StoreInfos) + checkContains(t, ci.StoreInfos, []string{"store1", "restore2", "store3", "store4"}) } func TestParsePath(t *testing.T) { @@ -474,7 +499,7 @@ func TestMultiStore_Pruning(t *testing.T) { for _, v := range tc.deleted { _, err := ms.CacheMultiStoreWithVersion(v) - require.Error(t, err, "expected error when loading height: %d", v) + require.NoError(t, err, "expected error when loading height: %d", v) } }) } @@ -510,7 +535,7 @@ func TestMultiStore_PruningRestart(t *testing.T) { for _, v := range pruneHeights { _, err := ms.CacheMultiStoreWithVersion(v) - require.Error(t, err, "expected error when loading height: %d", v) + require.NoError(t, err, "expected error when loading height: %d", v) } } @@ -796,8 +821,10 @@ func newMultiStoreWithModifiedMounts(db dbm.DB, pruningOpts types.PruningOptions store.MountStoreWithDB(types.NewKVStoreKey("store1"), types.StoreTypeIAVL, nil) store.MountStoreWithDB(types.NewKVStoreKey("restore2"), types.StoreTypeIAVL, nil) store.MountStoreWithDB(types.NewKVStoreKey("store3"), types.StoreTypeIAVL, nil) + store.MountStoreWithDB(types.NewKVStoreKey("store4"), types.StoreTypeIAVL, nil) upgrades := &types.StoreUpgrades{ + Added: []string{"store4"}, Renamed: []types.StoreRename{{ OldKey: "store2", NewKey: "restore2", diff --git a/store/types/store.go b/store/types/store.go index 989ae94e8c42..f78fcad3f402 100644 --- a/store/types/store.go +++ b/store/types/store.go @@ -9,6 +9,7 @@ import ( snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" "github.com/cosmos/cosmos-sdk/types/kv" + tmstrings "github.com/tendermint/tendermint/libs/strings" ) type Store interface { @@ -44,6 +45,7 @@ type Queryable interface { // StoreUpgrades defines a series of transformations to apply the multistore db upon load type StoreUpgrades struct { + Added []string `json:"added"` Renamed []StoreRename `json:"renamed"` Deleted []string `json:"deleted"` } @@ -63,6 +65,14 @@ type StoreRename struct { NewKey string `json:"new_key"` } +// IsDeleted returns true if the given key should be added +func (s *StoreUpgrades) IsAdded(key string) bool { + if s == nil { + return false + } + return tmstrings.StringInSlice(key, s.Added) +} + // IsDeleted returns true if the given key should be deleted func (s *StoreUpgrades) IsDeleted(key string) bool { if s == nil {