From bd6cb722d828b14b96799c51366f61e9eae1fe3d Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Fri, 24 Feb 2023 15:00:18 +0100 Subject: [PATCH 1/3] chore: remove store v2alpha1 --- CHANGELOG.md | 6 +- store/transient/store_test.go | 2 +- store/v2alpha1/dbadapter/store.go | 92 --- store/v2alpha1/dbadapter/store_test.go | 105 --- store/v2alpha1/mem/store.go | 45 -- store/v2alpha1/mem/store_test.go | 39 - store/v2alpha1/multi/cache_store.go | 36 - store/v2alpha1/multi/doc.go | 19 - store/v2alpha1/multi/migration.go | 68 -- store/v2alpha1/multi/migration_test.go | 107 --- store/v2alpha1/multi/proof.go | 52 -- store/v2alpha1/multi/proof_test.go | 125 ---- store/v2alpha1/multi/snapshot.go | 172 ----- store/v2alpha1/multi/snapshot_test.go | 317 -------- store/v2alpha1/multi/store.go | 915 ----------------------- store/v2alpha1/multi/store_test.go | 986 ------------------------- store/v2alpha1/multi/sub_store.go | 117 --- store/v2alpha1/multi/test_util.go | 88 --- store/v2alpha1/multi/view_store.go | 178 ----- store/v2alpha1/smt/ics23.go | 124 ---- store/v2alpha1/smt/ics23_test.go | 108 --- store/v2alpha1/smt/proof.go | 93 --- store/v2alpha1/smt/proof_test.go | 69 -- store/v2alpha1/smt/store.go | 143 ---- store/v2alpha1/smt/store_test.go | 46 -- store/v2alpha1/transient/store.go | 47 -- store/v2alpha1/transient/store_test.go | 27 - store/v2alpha1/types.go | 111 --- 28 files changed, 6 insertions(+), 4231 deletions(-) delete mode 100644 store/v2alpha1/dbadapter/store.go delete mode 100644 store/v2alpha1/dbadapter/store_test.go delete mode 100644 store/v2alpha1/mem/store.go delete mode 100644 store/v2alpha1/mem/store_test.go delete mode 100644 store/v2alpha1/multi/cache_store.go delete mode 100644 store/v2alpha1/multi/doc.go delete mode 100644 store/v2alpha1/multi/migration.go delete mode 100644 store/v2alpha1/multi/migration_test.go delete mode 100644 store/v2alpha1/multi/proof.go delete mode 100644 store/v2alpha1/multi/proof_test.go delete mode 100644 store/v2alpha1/multi/snapshot.go delete mode 100644 store/v2alpha1/multi/snapshot_test.go delete mode 100644 store/v2alpha1/multi/store.go delete mode 100644 store/v2alpha1/multi/store_test.go delete mode 100644 store/v2alpha1/multi/sub_store.go delete mode 100644 store/v2alpha1/multi/test_util.go delete mode 100644 store/v2alpha1/multi/view_store.go delete mode 100644 store/v2alpha1/smt/ics23.go delete mode 100644 store/v2alpha1/smt/ics23_test.go delete mode 100644 store/v2alpha1/smt/proof.go delete mode 100644 store/v2alpha1/smt/proof_test.go delete mode 100644 store/v2alpha1/smt/store.go delete mode 100644 store/v2alpha1/smt/store_test.go delete mode 100644 store/v2alpha1/transient/store.go delete mode 100644 store/v2alpha1/transient/store_test.go delete mode 100644 store/v2alpha1/types.go diff --git a/CHANGELOG.md b/CHANGELOG.md index ee70a2a12afb..e4b6e9e207cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,12 +37,16 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## [Unreleased] +## Improvements + +* (store) [#]() Remove unmaintained and experimental `store/v2alpha1`. +* (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.10](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.46.10) - 2022-02-16 ### 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/store/transient/store_test.go b/store/transient/store_test.go index 900e6cb7a918..48324aa9491d 100644 --- a/store/transient/store_test.go +++ b/store/transient/store_test.go @@ -8,7 +8,7 @@ import ( pruningtypes "github.com/cosmos/cosmos-sdk/pruning/types" "github.com/cosmos/cosmos-sdk/store/transient" - types "github.com/cosmos/cosmos-sdk/store/v2alpha1" + "github.com/cosmos/cosmos-sdk/store/types" ) var k, v = []byte("hello"), []byte("world") diff --git a/store/v2alpha1/dbadapter/store.go b/store/v2alpha1/dbadapter/store.go deleted file mode 100644 index 1cbd6c83585b..000000000000 --- a/store/v2alpha1/dbadapter/store.go +++ /dev/null @@ -1,92 +0,0 @@ -package dbadapter - -import ( - "io" - - dbm "github.com/cosmos/cosmos-sdk/db" - dbutil "github.com/cosmos/cosmos-sdk/internal/db" - "github.com/cosmos/cosmos-sdk/store/cachekv" - "github.com/cosmos/cosmos-sdk/store/listenkv" - "github.com/cosmos/cosmos-sdk/store/tracekv" - "github.com/cosmos/cosmos-sdk/store/types" -) - -var _ types.KVStore = Store{} - -// Wrapper type for dbm.Db with implementation of KVStore -type Store struct { - DB dbm.DBReadWriter -} - -// Get wraps the underlying DB's Get method panicing on error. -func (dsa Store) Get(key []byte) []byte { - v, err := dsa.DB.Get(key) - if err != nil { - panic(err) - } - - return v -} - -// Has wraps the underlying DB's Has method panicing on error. -func (dsa Store) Has(key []byte) bool { - ok, err := dsa.DB.Has(key) - if err != nil { - panic(err) - } - - return ok -} - -// Set wraps the underlying DB's Set method panicing on error. -func (dsa Store) Set(key, value []byte) { - types.AssertValidKey(key) - if err := dsa.DB.Set(key, value); err != nil { - panic(err) - } -} - -// Delete wraps the underlying DB's Delete method panicing on error. -func (dsa Store) Delete(key []byte) { - if err := dsa.DB.Delete(key); err != nil { - panic(err) - } -} - -// Iterator wraps the underlying DB's Iterator method panicing on error. -func (dsa Store) Iterator(start, end []byte) types.Iterator { - iter, err := dsa.DB.Iterator(start, end) - if err != nil { - panic(err) - } - return dbutil.DBToStoreIterator(iter) -} - -// ReverseIterator wraps the underlying DB's ReverseIterator method panicing on error. -func (dsa Store) ReverseIterator(start, end []byte) types.Iterator { - iter, err := dsa.DB.ReverseIterator(start, end) - if err != nil { - panic(err) - } - return dbutil.DBToStoreIterator(iter) -} - -// GetStoreType returns the type of the store. -func (dsa Store) GetStoreType() types.StoreType { - return types.StoreTypeDB -} - -// CacheWrap branches the underlying store. -func (dsa Store) CacheWrap() types.CacheWrap { - return cachekv.NewStore(dsa) -} - -// CacheWrapWithTrace implements KVStore. -func (dsa Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { - return cachekv.NewStore(tracekv.NewStore(dsa, w, tc)) -} - -// CacheWrapWithListeners implements the CacheWrapper interface. -func (dsa Store) CacheWrapWithListeners(storeKey types.StoreKey, listeners []types.WriteListener) types.CacheWrap { - return cachekv.NewStore(listenkv.NewStore(dsa, storeKey, listeners)) -} diff --git a/store/v2alpha1/dbadapter/store_test.go b/store/v2alpha1/dbadapter/store_test.go deleted file mode 100644 index 9bfa68518ab0..000000000000 --- a/store/v2alpha1/dbadapter/store_test.go +++ /dev/null @@ -1,105 +0,0 @@ -package dbadapter_test - -import ( - "bytes" - "errors" - "testing" - - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/store/cachekv" - types "github.com/cosmos/cosmos-sdk/store/v2alpha1" - "github.com/cosmos/cosmos-sdk/store/v2alpha1/dbadapter" - mocks "github.com/cosmos/cosmos-sdk/tests/mocks/db" -) - -var errFoo = errors.New("dummy") - -func TestAccessors(t *testing.T) { - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - - mockDB := mocks.NewMockDBReadWriter(mockCtrl) - store := dbadapter.Store{mockDB} - key := []byte("test") - value := []byte("testvalue") - - require.Panics(t, func() { store.Set(nil, []byte("value")) }, "setting a nil key should panic") - require.Panics(t, func() { store.Set([]byte(""), []byte("value")) }, "setting an empty key should panic") - - require.Equal(t, types.StoreTypeDB, store.GetStoreType()) - - retFoo := []byte("xxx") - mockDB.EXPECT().Get(gomock.Eq(key)).Times(1).Return(retFoo, nil) - require.True(t, bytes.Equal(retFoo, store.Get(key))) - require.Equal(t, []byte{1, 2, 3}, []byte{1, 2, 3}) - - mockDB.EXPECT().Get(gomock.Eq(key)).Times(1).Return(nil, errFoo) - require.Panics(t, func() { store.Get(key) }) - - mockDB.EXPECT().Has(gomock.Eq(key)).Times(1).Return(true, nil) - require.True(t, store.Has(key)) - - mockDB.EXPECT().Has(gomock.Eq(key)).Times(1).Return(false, nil) - require.False(t, store.Has(key)) - - mockDB.EXPECT().Has(gomock.Eq(key)).Times(1).Return(false, errFoo) - require.Panics(t, func() { store.Has(key) }) - - mockDB.EXPECT().Set(gomock.Eq(key), gomock.Eq(value)).Times(1).Return(nil) - require.NotPanics(t, func() { store.Set(key, value) }) - - mockDB.EXPECT().Set(gomock.Eq(key), gomock.Eq(value)).Times(1).Return(errFoo) - require.Panics(t, func() { store.Set(key, value) }) - - mockDB.EXPECT().Delete(gomock.Eq(key)).Times(1).Return(nil) - require.NotPanics(t, func() { store.Delete(key) }) - - mockDB.EXPECT().Delete(gomock.Eq(key)).Times(1).Return(errFoo) - require.Panics(t, func() { store.Delete(key) }) -} - -func TestIterators(t *testing.T) { - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - - mockDB := mocks.NewMockDBReadWriter(mockCtrl) - store := dbadapter.Store{mockDB} - key := []byte("test") - value := []byte("testvalue") - - start, end := key, []byte("test_end") - - mockDB.EXPECT().Iterator(gomock.Eq(start), gomock.Eq(end)).Times(1).Return(nil, errFoo) - require.Panics(t, func() { store.Iterator(start, end) }) - - mockDB.EXPECT().ReverseIterator(gomock.Eq(start), gomock.Eq(end)).Times(1).Return(nil, errFoo) - require.Panics(t, func() { store.ReverseIterator(start, end) }) - - mockIter := mocks.NewMockIterator(mockCtrl) - mockIter.EXPECT().Next().Times(1).Return(true) - mockIter.EXPECT().Key().Times(1).Return(key) - mockIter.EXPECT().Value().Times(1).Return(value) - - mockDB.EXPECT().Iterator(gomock.Eq(start), gomock.Eq(end)).Times(1).Return(mockIter, nil) - iter := store.Iterator(start, end) - - require.Equal(t, key, iter.Key()) - require.Equal(t, value, iter.Value()) -} - -func TestCacheWraps(t *testing.T) { - mockCtrl := gomock.NewController(t) - mockDB := mocks.NewMockDBReadWriter(mockCtrl) - store := dbadapter.Store{mockDB} - - cacheWrapper := store.CacheWrap() - require.IsType(t, &cachekv.Store{}, cacheWrapper) - - cacheWrappedWithTrace := store.CacheWrapWithTrace(nil, nil) - require.IsType(t, &cachekv.Store{}, cacheWrappedWithTrace) - - cacheWrappedWithListeners := store.CacheWrapWithListeners(nil, nil) - require.IsType(t, &cachekv.Store{}, cacheWrappedWithListeners) -} diff --git a/store/v2alpha1/mem/store.go b/store/v2alpha1/mem/store.go deleted file mode 100644 index 3c8fa82bbaba..000000000000 --- a/store/v2alpha1/mem/store.go +++ /dev/null @@ -1,45 +0,0 @@ -package mem - -import ( - dbm "github.com/cosmos/cosmos-sdk/db" - "github.com/cosmos/cosmos-sdk/db/memdb" - pruningtypes "github.com/cosmos/cosmos-sdk/pruning/types" - "github.com/cosmos/cosmos-sdk/store/types" - "github.com/cosmos/cosmos-sdk/store/v2alpha1/dbadapter" -) - -var ( - _ types.KVStore = (*Store)(nil) - _ types.Committer = (*Store)(nil) -) - -// Store implements an in-memory only KVStore. Entries are persisted between -// commits and thus between blocks. State in Memory store is not committed as part of app state but maintained privately by each node -type Store struct { - dbadapter.Store - conn dbm.DBConnection -} - -// NewStore constructs a new in-memory store. -func NewStore() *Store { - db := memdb.NewDB() - return &Store{ - Store: dbadapter.Store{DB: db.ReadWriter()}, - conn: db, - } -} - -// GetStoreType returns the Store's type. -func (s Store) GetStoreType() types.StoreType { - return types.StoreTypeMemory -} - -// Commit commits to the underlying DB. -func (s *Store) Commit() (id types.CommitID) { - return -} - -func (s *Store) SetPruning(pruningtypes.PruningOptions) {} -func (s *Store) GetPruning() pruningtypes.PruningOptions { return pruningtypes.PruningOptions{} } - -func (s Store) LastCommitID() (id types.CommitID) { return } diff --git a/store/v2alpha1/mem/store_test.go b/store/v2alpha1/mem/store_test.go deleted file mode 100644 index 297e2a75df76..000000000000 --- a/store/v2alpha1/mem/store_test.go +++ /dev/null @@ -1,39 +0,0 @@ -package mem_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - - types "github.com/cosmos/cosmos-sdk/store/v2alpha1" - "github.com/cosmos/cosmos-sdk/store/v2alpha1/mem" -) - -func TestStore(t *testing.T) { - store := mem.NewStore() - key, value := []byte("key"), []byte("value") - - require.Equal(t, types.StoreTypeMemory, store.GetStoreType()) - - require.Nil(t, store.Get(key)) - store.Set(key, value) - require.Equal(t, value, store.Get(key)) - - newValue := []byte("newValue") - store.Set(key, newValue) - require.Equal(t, newValue, store.Get(key)) - - store.Delete(key) - require.Nil(t, store.Get(key)) -} - -func TestCommit(t *testing.T) { - store := mem.NewStore() - key, value := []byte("key"), []byte("value") - - store.Set(key, value) - id := store.Commit() - require.True(t, id.IsZero()) - require.True(t, store.LastCommitID().IsZero()) - require.Equal(t, value, store.Get(key)) -} diff --git a/store/v2alpha1/multi/cache_store.go b/store/v2alpha1/multi/cache_store.go deleted file mode 100644 index 5d7ee786da43..000000000000 --- a/store/v2alpha1/multi/cache_store.go +++ /dev/null @@ -1,36 +0,0 @@ -package multi - -import ( - "github.com/cosmos/cosmos-sdk/store/cachekv" - types "github.com/cosmos/cosmos-sdk/store/v2alpha1" -) - -// GetKVStore implements BasicMultiStore. -func (cs *cacheStore) GetKVStore(skey types.StoreKey) types.KVStore { - key := skey.Name() - sub, has := cs.substores[key] - if !has { - sub = cachekv.NewStore(cs.source.GetKVStore(skey)) - cs.substores[key] = sub - } - // Wrap with trace/listen if needed. Note: we don't cache this, so users must get a new substore after - // modifying tracers/listeners. - return cs.wrapTraceListen(sub, skey) -} - -// Write implements CacheMultiStore. -func (cs *cacheStore) Write() { - for _, sub := range cs.substores { - sub.Write() - } -} - -// CacheMultiStore implements BasicMultiStore. -// This recursively wraps the CacheMultiStore in another cache store. -func (cs *cacheStore) CacheMultiStore() types.CacheMultiStore { - return &cacheStore{ - source: cs, - substores: map[string]types.CacheKVStore{}, - traceListenMixin: newTraceListenMixin(), - } -} diff --git a/store/v2alpha1/multi/doc.go b/store/v2alpha1/multi/doc.go deleted file mode 100644 index 87f5b88705c9..000000000000 --- a/store/v2alpha1/multi/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -// This package provides concrete implementations of the store/v2alpha1 "MultiStore" types, including -// CommitMultiStore, CacheMultiStore, and BasicMultiStore (as read-only stores at past versions). -// -// Substores are declared as part of a schema within StoreOptions. -// The schema cannot be changed once a CommitMultiStore is initialized, and changes to the schema must be done -// by migrating via StoreOptions.Upgrades. If a past version is accessed, it will be loaded with the past schema. -// Stores may be declared as StoreTypePersistent, StoreTypeMemory (not persisted after close), or -// StoreTypeTransient (not persisted across commits). Non-persistent substores cannot be migrated or accessed -// in past versions. -// -// A declared persistent substore is initially empty and stores nothing in the backing DB until a value is set. -// A non-empty store is stored within a prefixed subdomain of the backing DB (using db/prefix). -// If the MultiStore is configured to use a separate DBConnection for StateCommitmentDB, it will store the -// state commitment (SC) store (as an SMT) in subdomains there, and the "flat" state is stored in the main DB. -// Each substore's SC is allocated as an independent SMT, and query proofs contain two components: a proof -// of a key's (non)existence within the substore SMT, and a proof of the substore's existence within the -// MultiStore (using the Merkle map proof spec (TendermintSpec)). - -package multi diff --git a/store/v2alpha1/multi/migration.go b/store/v2alpha1/multi/migration.go deleted file mode 100644 index 7481b421ac3b..000000000000 --- a/store/v2alpha1/multi/migration.go +++ /dev/null @@ -1,68 +0,0 @@ -package multi - -import ( - dbm "github.com/cosmos/cosmos-sdk/db" - "github.com/cosmos/cosmos-sdk/store/iavl" - "github.com/cosmos/cosmos-sdk/store/mem" - v1Store "github.com/cosmos/cosmos-sdk/store/rootmulti" - "github.com/cosmos/cosmos-sdk/store/transient" - "github.com/cosmos/cosmos-sdk/store/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -// MigrateFromV1 will migrate the state from iavl to smt -func MigrateFromV1(rootMultiStore *v1Store.Store, store2db dbm.DBConnection, storeConfig StoreConfig) (*Store, error) { - type namedStore struct { - *iavl.Store - name string - } - var stores []namedStore - for _, storeKey := range rootMultiStore.StoreKeysByName() { - keyName := storeKey.Name() - switch store := rootMultiStore.GetStoreByName(keyName).(type) { - case *iavl.Store: - err := storeConfig.RegisterSubstore(keyName, types.StoreTypePersistent) - if err != nil { - return nil, err - } - stores = append(stores, namedStore{name: keyName, Store: store}) - case *transient.Store, *mem.Store: - continue - default: - return nil, sdkerrors.Wrapf(sdkerrors.ErrLogic, "don't know how to migrate store %q of type %T", keyName, store) - } - } - - // creating the new store of smt tree - rootStore, err := NewStore(store2db, storeConfig) - if err != nil { - return nil, err - } - - // if version is 0 there is no state data to commit - if rootMultiStore.LastCommitID().Version == 0 { - return rootStore, nil - } - - // iterate through the rootmulti stores and save the key/values into smt tree - for _, store := range stores { - subStore, err := rootStore.getSubstore(store.name) - if err != nil { - return nil, err - } - // iterate all iavl tree node key/values - iterator := store.Iterator(nil, nil) - for ; iterator.Valid(); iterator.Next() { - // set the iavl key,values into smt node - subStore.Set(iterator.Key(), iterator.Value()) - } - } - - // commit the all key/values from iavl to smt tree (SMT Store) - _, err = rootStore.commit(uint64(rootMultiStore.LastCommitID().Version)) - if err != nil { - return nil, err - } - - return rootStore, nil -} diff --git a/store/v2alpha1/multi/migration_test.go b/store/v2alpha1/multi/migration_test.go deleted file mode 100644 index bf7b0f921cd0..000000000000 --- a/store/v2alpha1/multi/migration_test.go +++ /dev/null @@ -1,107 +0,0 @@ -package multi - -import ( - "encoding/binary" - "fmt" - "math/rand" - "testing" - - "github.com/cosmos/cosmos-sdk/db/memdb" - "github.com/cosmos/cosmos-sdk/store/iavl" - "github.com/cosmos/cosmos-sdk/store/rootmulti" - "github.com/cosmos/cosmos-sdk/store/types" - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/libs/log" - dbm "github.com/tendermint/tm-db" -) - -func TestMigrationV2(t *testing.T) { - r := rand.New(rand.NewSource(49872768940)) - - // setup a rootmulti store - db := dbm.NewMemDB() - v1Store := rootmulti.NewStore(db, log.NewNopLogger()) - - // mount the kvStores - var keys []*types.KVStoreKey - for i := uint8(0); i < 10; i++ { - key := types.NewKVStoreKey(fmt.Sprintf("store%v", i)) - v1Store.MountStoreWithDB(key, types.StoreTypeIAVL, nil) - keys = append(keys, key) - } - - err := v1Store.LoadLatestVersion() - require.Nil(t, err) - - // setup a random test data - for _, key := range keys { - store := v1Store.GetStore(key).(*iavl.Store) - store.Set([]byte("temp_data"), []byte("one")) - - for i := 0; i < len(keys); i++ { - k := make([]byte, 8) - v := make([]byte, 1024) - binary.BigEndian.PutUint64(k, uint64(i)) - _, err := r.Read(v) - if err != nil { - panic(err) - } - store.Set(k, v) - } - } - - testCases := []struct { - testName string - emptyStore bool - }{ - { - "Migration With Empty Store", - true, - }, - { - "Migration From Root Multi Store (IAVL) to SMT ", - false, - }, - } - - for _, testCase := range testCases { - if !testCase.emptyStore { - v1Store.Commit() - } - - // setup a new root store of smt - db2 := memdb.NewDB() - storeConfig := DefaultStoreConfig() - // migrating the iavl store (v1) to smt store (v2) - v2Store, err := MigrateFromV1(v1Store, db2, storeConfig) - require.NoError(t, err) - - for _, key := range keys { - v2StoreKVStore := v2Store.GetKVStore(key) - if testCase.emptyStore { - // check the empty store - require.Nil(t, v2StoreKVStore.Get([]byte("temp_data"))) - } else { - require.Equal(t, v2StoreKVStore.Get([]byte("temp_data")), []byte("one")) - } - require.Equal(t, v2Store.LastCommitID().Version, v1Store.LastCommitID().Version) - } - err = v2Store.Close() - require.NoError(t, err) - } -} - -// TestMigrateV2ForEmptyStore checking empty store migration -func TestMigrateV2ForEmptyStore(t *testing.T) { - // setup a rootmulti store - db := dbm.NewMemDB() - v1Store := rootmulti.NewStore(db, log.NewNopLogger()) - err := v1Store.LoadLatestVersion() - require.Nil(t, err) - db2 := memdb.NewDB() - storeConfig := DefaultStoreConfig() - // migrating the iavl store (v1) to smt store (v2) - v2Store, err := MigrateFromV1(v1Store, db2, storeConfig) - require.NoError(t, err) - require.Equal(t, v2Store.LastCommitID(), v1Store.LastCommitID()) -} diff --git a/store/v2alpha1/multi/proof.go b/store/v2alpha1/multi/proof.go deleted file mode 100644 index 1e8b538c5373..000000000000 --- a/store/v2alpha1/multi/proof.go +++ /dev/null @@ -1,52 +0,0 @@ -package multi - -import ( - "crypto/sha256" - - "github.com/tendermint/tendermint/crypto/merkle" - tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" - - types "github.com/cosmos/cosmos-sdk/store/v2alpha1" - "github.com/cosmos/cosmos-sdk/store/v2alpha1/smt" -) - -// DefaultProofRuntime returns a ProofRuntime supporting SMT and simple merkle proofs. -func DefaultProofRuntime() (prt *merkle.ProofRuntime) { - prt = merkle.NewProofRuntime() - prt.RegisterOpDecoder(types.ProofOpSMTCommitment, types.CommitmentOpDecoder) - prt.RegisterOpDecoder(types.ProofOpSimpleMerkleCommitment, types.CommitmentOpDecoder) - return prt -} - -// Prove commitment of key within an smt store and return ProofOps -func proveKey(s *smt.Store, key []byte) (*tmcrypto.ProofOps, error) { - var ret tmcrypto.ProofOps - keyProof, err := s.GetProofICS23(key) - if err != nil { - return nil, err - } - hkey := sha256.Sum256(key) - ret.Ops = append(ret.Ops, types.NewSmtCommitmentOp(hkey[:], keyProof).ProofOp()) - return &ret, nil -} - -// GetProof returns ProofOps containing: a proof for the given key within this substore; -// and a proof of the substore's existence within the MultiStore. -func (s *viewSubstore) GetProof(key []byte) (*tmcrypto.ProofOps, error) { - ret, err := proveKey(s.stateCommitmentStore, key) - if err != nil { - return nil, err - } - - // Prove commitment of substore within root store - storeHashes, err := s.root.getMerkleRoots() - if err != nil { - return nil, err - } - storeProof, err := types.ProofOpFromMap(storeHashes, s.name) - if err != nil { - return nil, err - } - ret.Ops = append(ret.Ops, storeProof) - return ret, nil -} diff --git a/store/v2alpha1/multi/proof_test.go b/store/v2alpha1/multi/proof_test.go deleted file mode 100644 index 86e9fbfd2ed0..000000000000 --- a/store/v2alpha1/multi/proof_test.go +++ /dev/null @@ -1,125 +0,0 @@ -package multi - -import ( - "crypto/sha256" - "testing" - - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - - "github.com/cosmos/cosmos-sdk/db/memdb" - "github.com/cosmos/cosmos-sdk/store/v2alpha1/smt" -) - -// We hash keys produce SMT paths, so reflect that here -func keyPath(prefix, key string) string { - hashed := sha256.Sum256([]byte(key)) - return prefix + string(hashed[:]) -} - -func TestVerifySMTStoreProof(t *testing.T) { - // Create main tree for testing. - txn := memdb.NewDB().ReadWriter() - store := smt.NewStore(txn) - store.Set([]byte("MYKEY"), []byte("MYVALUE")) - root := store.Root() - - res, err := proveKey(store, []byte("MYKEY")) - require.NoError(t, err) - - // Verify good proof. - prt := DefaultProofRuntime() - err = prt.VerifyValue(res, root, keyPath("/", "MYKEY"), []byte("MYVALUE")) - require.NoError(t, err) - - // Fail to verify bad proofs. - err = prt.VerifyValue(res, root, keyPath("/", "MYKEY_NOT"), []byte("MYVALUE")) - require.Error(t, err) - - err = prt.VerifyValue(res, root, keyPath("/", "MYKEY/MYKEY"), []byte("MYVALUE")) - require.Error(t, err) - - err = prt.VerifyValue(res, root, keyPath("", "MYKEY"), []byte("MYVALUE")) - require.Error(t, err) - - err = prt.VerifyValue(res, root, keyPath("/", "MYKEY"), []byte("MYVALUE_NOT")) - require.Error(t, err) - - err = prt.VerifyValue(res, root, keyPath("/", "MYKEY"), []byte(nil)) - require.Error(t, err) -} - -func TestVerifyMultiStoreQueryProof(t *testing.T) { - db := memdb.NewDB() - store, err := NewStore(db, simpleStoreConfig(t)) - require.NoError(t, err) - - substore := store.GetKVStore(skey_1) - substore.Set([]byte("MYKEY"), []byte("MYVALUE")) - cid := store.Commit() - - res := store.Query(abci.RequestQuery{ - Path: "/store1/key", // required path to get key/value+proof - Data: []byte("MYKEY"), - Prove: true, - }) - require.NotNil(t, res.ProofOps) - - // Verify good proofs. - prt := DefaultProofRuntime() - err = prt.VerifyValue(res.ProofOps, cid.Hash, keyPath("/store1/", "MYKEY"), []byte("MYVALUE")) - require.NoError(t, err) - - err = prt.VerifyValue(res.ProofOps, cid.Hash, keyPath("/store1/", "MYKEY"), []byte("MYVALUE")) - require.NoError(t, err) - - // Fail to verify bad proofs. - err = prt.VerifyValue(res.ProofOps, cid.Hash, keyPath("/store1/", "MYKEY_NOT"), []byte("MYVALUE")) - require.Error(t, err) - - err = prt.VerifyValue(res.ProofOps, cid.Hash, keyPath("/store1/MYKEY/", "MYKEY"), []byte("MYVALUE")) - require.Error(t, err) - - err = prt.VerifyValue(res.ProofOps, cid.Hash, keyPath("store1/", "MYKEY"), []byte("MYVALUE")) - require.Error(t, err) - - err = prt.VerifyValue(res.ProofOps, cid.Hash, keyPath("/", "MYKEY"), []byte("MYVALUE")) - require.Error(t, err) - - err = prt.VerifyValue(res.ProofOps, cid.Hash, keyPath("/store1/", "MYKEY"), []byte("MYVALUE_NOT")) - require.Error(t, err) - - err = prt.VerifyValue(res.ProofOps, cid.Hash, keyPath("/store1/", "MYKEY"), []byte(nil)) - require.Error(t, err) -} - -func TestVerifyMultiStoreQueryProofAbsence(t *testing.T) { - db := memdb.NewDB() - store, err := NewStore(db, simpleStoreConfig(t)) - require.NoError(t, err) - - substore := store.GetKVStore(skey_1) - substore.Set([]byte("MYKEY"), []byte("MYVALUE")) - cid := store.Commit() - - res := store.Query(abci.RequestQuery{ - Path: "/store1/key", // required path to get key/value+proof - Data: []byte("MYABSENTKEY"), - Prove: true, - }) - require.NotNil(t, res.ProofOps) - - // Verify good proof. - prt := DefaultProofRuntime() - err = prt.VerifyAbsence(res.ProofOps, cid.Hash, keyPath("/store1/", "MYABSENTKEY")) - require.NoError(t, err) - - // Fail to verify bad proofs. - prt = DefaultProofRuntime() - err = prt.VerifyAbsence(res.ProofOps, cid.Hash, keyPath("/", "MYABSENTKEY")) - require.Error(t, err) - - prt = DefaultProofRuntime() - err = prt.VerifyValue(res.ProofOps, cid.Hash, keyPath("/store1/", "MYABSENTKEY"), []byte("")) - require.Error(t, err) -} diff --git a/store/v2alpha1/multi/snapshot.go b/store/v2alpha1/multi/snapshot.go deleted file mode 100644 index 434c5e1c6fc5..000000000000 --- a/store/v2alpha1/multi/snapshot.go +++ /dev/null @@ -1,172 +0,0 @@ -package multi - -import ( - "bytes" - "fmt" - "io" - "sort" - - protoio "github.com/gogo/protobuf/io" - - "github.com/cosmos/cosmos-sdk/snapshots" - snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" - storetypes "github.com/cosmos/cosmos-sdk/store/types" - types "github.com/cosmos/cosmos-sdk/store/v2alpha1" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -// Snapshot implements snapshottypes.Snapshotter. -func (rs *Store) Snapshot(height uint64, protoWriter protoio.Writer) error { - if height == 0 { - return snapshottypes.ErrInvalidSnapshotVersion - } - if height > uint64(rs.LastCommitID().Version) { - return sdkerrors.Wrapf(sdkerrors.ErrLogic, "cannot snapshot future height %v", height) - } - - // get the saved snapshot at height - vs, err := rs.getView(int64(height)) - if err != nil { - return sdkerrors.Wrap(err, fmt.Sprintf("error while get the version at height %d", height)) - } - - // sending the snapshot store schema - var storeByteKeys [][]byte - for sKey := range vs.schema { - if vs.schema[sKey] == storetypes.StoreTypePersistent { - storeByteKeys = append(storeByteKeys, []byte(sKey)) - } - } - - sort.Slice(storeByteKeys, func(i, j int) bool { - return bytes.Compare(storeByteKeys[i], storeByteKeys[j]) == -1 - }) - - err = protoWriter.WriteMsg(&snapshottypes.SnapshotItem{ - Item: &snapshottypes.SnapshotItem_Schema{ - Schema: &snapshottypes.SnapshotSchema{ - Keys: storeByteKeys, - }, - }, - }) - if err != nil { - return err - } - - for _, sKey := range storeByteKeys { - subStore, err := vs.getSubstore(string(sKey)) - if err != nil { - return err - } - - err = protoWriter.WriteMsg(&snapshottypes.SnapshotItem{ - Item: &snapshottypes.SnapshotItem_Store{ - Store: &snapshottypes.SnapshotStoreItem{ - Name: string(sKey), - }, - }, - }) - if err != nil { - return err - } - - iter := subStore.Iterator(nil, nil) - for ; iter.Valid(); iter.Next() { - err = protoWriter.WriteMsg(&snapshottypes.SnapshotItem{ - Item: &snapshottypes.SnapshotItem_KV{ - KV: &snapshottypes.SnapshotKVItem{ - Key: iter.Key(), - Value: iter.Value(), - }, - }, - }) - if err != nil { - return err - } - } - - err = iter.Close() - if err != nil { - return err - } - } - - return nil -} - -// Restore implements snapshottypes.Snapshotter. -func (rs *Store) Restore( - height uint64, format uint32, protoReader protoio.Reader, -) (snapshottypes.SnapshotItem, error) { - if err := snapshots.ValidRestoreHeight(format, height); err != nil { - return snapshottypes.SnapshotItem{}, err - } - - if rs.LastCommitID().Version != 0 { - return snapshottypes.SnapshotItem{}, sdkerrors.Wrapf(sdkerrors.ErrLogic, "cannot restore snapshot for non empty store at height %v", height) - } - - var subStore *substore - storeSchemaReceived := false - - var snapshotItem snapshottypes.SnapshotItem - -loop: - for { - snapshotItem = snapshottypes.SnapshotItem{} - err := protoReader.ReadMsg(&snapshotItem) - if err == io.EOF { - break - } else if err != nil { - return snapshottypes.SnapshotItem{}, sdkerrors.Wrap(err, "invalid protobuf message") - } - - switch item := snapshotItem.Item.(type) { - case *snapshottypes.SnapshotItem_Schema: - receivedStoreSchema := make(StoreSchema, len(item.Schema.GetKeys())) - storeSchemaReceived = true - for _, sKey := range item.Schema.GetKeys() { - receivedStoreSchema[string(sKey)] = types.StoreTypePersistent - } - - if !rs.schema.equal(receivedStoreSchema) { - return snapshottypes.SnapshotItem{}, sdkerrors.Wrap(sdkerrors.ErrLogic, "received schema does not match app schema") - } - - case *snapshottypes.SnapshotItem_Store: - storeName := item.Store.GetName() - // checking the store schema is received or not - if !storeSchemaReceived { - return snapshottypes.SnapshotItem{}, sdkerrors.Wrapf(sdkerrors.ErrLogic, "received store name before store schema %s", storeName) - } - // checking the store schema exists or not - if _, has := rs.schema[storeName]; !has { - return snapshottypes.SnapshotItem{}, sdkerrors.Wrapf(sdkerrors.ErrLogic, "store is missing from schema %s", storeName) - } - - // get the substore - subStore, err = rs.getSubstore(storeName) - if err != nil { - return snapshottypes.SnapshotItem{}, sdkerrors.Wrap(err, fmt.Sprintf("error while getting the substore for key %s", storeName)) - } - - case *snapshottypes.SnapshotItem_KV: - if subStore == nil { - return snapshottypes.SnapshotItem{}, sdkerrors.Wrap(sdkerrors.ErrLogic, "received KV Item before store item") - } - // update the key/value SMT.Store - subStore.Set(item.KV.Key, item.KV.Value) - - default: - break loop - } - } - - // commit the all key/values to store - _, err := rs.commit(height) - if err != nil { - return snapshotItem, sdkerrors.Wrap(err, fmt.Sprintf("error during commit the store at height %d", height)) - } - - return snapshotItem, nil -} diff --git a/store/v2alpha1/multi/snapshot_test.go b/store/v2alpha1/multi/snapshot_test.go deleted file mode 100644 index 77637910e768..000000000000 --- a/store/v2alpha1/multi/snapshot_test.go +++ /dev/null @@ -1,317 +0,0 @@ -package multi - -import ( - "crypto/sha256" - "encoding/binary" - "encoding/hex" - "errors" - "fmt" - "io" - "math/rand" - "sort" - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - dbm "github.com/cosmos/cosmos-sdk/db" - "github.com/cosmos/cosmos-sdk/db/memdb" - pruningtypes "github.com/cosmos/cosmos-sdk/pruning/types" - "github.com/cosmos/cosmos-sdk/snapshots" - snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" - "github.com/cosmos/cosmos-sdk/store/types" -) - -func multiStoreConfig(t *testing.T, stores int) StoreConfig { - opts := DefaultStoreConfig() - opts.Pruning = pruningtypes.NewPruningOptions(pruningtypes.PruningNothing) - - for i := 0; i < stores; i++ { - sKey := types.NewKVStoreKey(fmt.Sprintf("store%d", i)) - require.NoError(t, opts.RegisterSubstore(sKey.Name(), types.StoreTypePersistent)) - } - - return opts -} - -func newMultiStoreWithGeneratedData(t *testing.T, db dbm.DBConnection, stores int, storeKeys uint64) *Store { - cfg := multiStoreConfig(t, stores) - store, err := NewStore(db, cfg) - require.NoError(t, err) - r := rand.New(rand.NewSource(49872768940)) // Fixed seed for deterministic tests - - var sKeys []string - for sKey := range store.schema { - sKeys = append(sKeys, sKey) - } - - sort.Slice(sKeys, func(i, j int) bool { - return strings.Compare(sKeys[i], sKeys[j]) == -1 - }) - - for _, sKey := range sKeys { - sStore, err := store.getSubstore(sKey) - require.NoError(t, err) - for i := uint64(0); i < storeKeys; i++ { - k := make([]byte, 8) - v := make([]byte, 1024) - binary.BigEndian.PutUint64(k, i) - _, err := r.Read(v) - if err != nil { - panic(err) - } - sStore.Set(k, v) - } - } - store.Commit() - return store -} - -func newMultiStoreWithBasicData(t *testing.T, db dbm.DBConnection, stores int) *Store { - cfg := multiStoreConfig(t, stores) - store, err := NewStore(db, cfg) - require.NoError(t, err) - - for sKey := range store.schema { - sStore, err := store.getSubstore(sKey) - require.NoError(t, err) - for k, v := range alohaData { - sStore.Set([]byte(k), []byte(v)) - } - } - - store.Commit() - return store -} - -func newMultiStore(t *testing.T, db dbm.DBConnection, stores int) *Store { - cfg := multiStoreConfig(t, stores) - store, err := NewStore(db, cfg) - require.NoError(t, err) - return store -} - -func TestMultistoreSnapshot_Errors(t *testing.T) { - store := newMultiStoreWithBasicData(t, memdb.NewDB(), 4) - testcases := map[string]struct { - height uint64 - expectType error - }{ - "0 height": {0, snapshottypes.ErrInvalidSnapshotVersion}, - "1 height": {1, nil}, - } - for name, tc := range testcases { - tc := tc - t.Run(name, func(t *testing.T) { - chunks := make(chan io.ReadCloser) - streamWriter := snapshots.NewStreamWriter(chunks) - err := store.Snapshot(tc.height, streamWriter) - if tc.expectType != nil { - assert.True(t, errors.Is(err, tc.expectType)) - } - }) - } -} - -func TestMultistoreRestore_Errors(t *testing.T) { - store := newMultiStoreWithBasicData(t, memdb.NewDB(), 4) - testcases := map[string]struct { - height uint64 - format uint32 - expectErrorType error - }{ - "0 height": {0, snapshottypes.CurrentFormat, nil}, - "0 format": {1, 0, snapshottypes.ErrUnknownFormat}, - "unknown format": {1, 9, snapshottypes.ErrUnknownFormat}, - } - for name, tc := range testcases { - tc := tc - t.Run(name, func(t *testing.T) { - _, err := store.Restore(tc.height, tc.format, nil) - require.Error(t, err) - if tc.expectErrorType != nil { - assert.True(t, errors.Is(err, tc.expectErrorType)) - } - }) - } -} - -func TestMultistoreSnapshot_Checksum(t *testing.T) { - store := newMultiStoreWithGeneratedData(t, memdb.NewDB(), 5, 10000) - version := uint64(store.LastCommitID().Version) - - testcases := []struct { - format uint32 - chunkHashes []string - }{ - {1, []string{ - "b0635a30d94d56b6cd1073fbfa109fa90b194d0ff2397659b00934c844a1f6fb", - "8c32e05f312cf2dee6b7d2bdb41e1a2bb2372697f25504e676af1718245d8b63", - "05dfef0e32c34ef3900300f9de51f228d7fb204fa8f4e4d0d1529f083d122029", - "77d30aeeb427b0bdcedf3639adde1e822c15233d652782e171125280875aa492", - "c00c3801da889ea4370f0e647ffe1e291bd47f500e2a7269611eb4cc198b993f", - "6d565eb28776631f3e3e764decd53436c3be073a8a01fa5434afd539f9ae6eda", - }}, - } - for _, tc := range testcases { - tc := tc - t.Run(fmt.Sprintf("Format %v", tc.format), func(t *testing.T) { - chunks := make(chan io.ReadCloser, 100) - hashes := []string{} - go func() { - streamWriter := snapshots.NewStreamWriter(chunks) - defer streamWriter.Close() - require.NotNil(t, streamWriter) - err := store.Snapshot(version, streamWriter) - require.NoError(t, err) - }() - hasher := sha256.New() - for chunk := range chunks { - hasher.Reset() - _, err := io.Copy(hasher, chunk) - require.NoError(t, err) - hashes = append(hashes, hex.EncodeToString(hasher.Sum(nil))) - } - assert.Equal(t, tc.chunkHashes, hashes, "Snapshot output for format %v has changed", tc.format) - }) - } -} - -func TestMultistoreSnapshotRestore(t *testing.T) { - source := newMultiStoreWithGeneratedData(t, memdb.NewDB(), 3, 4) - target := newMultiStore(t, memdb.NewDB(), 3) - require.Equal(t, source.LastCommitID().Version, int64(1)) - version := uint64(source.LastCommitID().Version) - // check for target store restore - require.Equal(t, target.LastCommitID().Version, int64(0)) - - dummyExtensionItem := snapshottypes.SnapshotItem{ - Item: &snapshottypes.SnapshotItem_Extension{ - Extension: &snapshottypes.SnapshotExtensionMeta{ - Name: "test", - Format: 1, - }, - }, - } - - chunks := make(chan io.ReadCloser, 100) - go func() { - streamWriter := snapshots.NewStreamWriter(chunks) - require.NotNil(t, streamWriter) - defer streamWriter.Close() - err := source.Snapshot(version, streamWriter) - require.NoError(t, err) - // write an extension metadata - err = streamWriter.WriteMsg(&dummyExtensionItem) - require.NoError(t, err) - }() - - streamReader, err := snapshots.NewStreamReader(chunks) - require.NoError(t, err) - nextItem, err := target.Restore(version, snapshottypes.CurrentFormat, streamReader) - require.NoError(t, err) - require.Equal(t, *dummyExtensionItem.GetExtension(), *nextItem.GetExtension()) - - assert.Equal(t, source.LastCommitID(), target.LastCommitID()) - - for sKey := range source.schema { - sourceSubStore, err := source.getSubstore(sKey) - require.NoError(t, err) - targetSubStore, err := target.getSubstore(sKey) - require.NoError(t, err) - require.Equal(t, sourceSubStore, targetSubStore) - } - - // checking snapshot restoring for store with existed schema and without existing versions - target3 := newMultiStore(t, memdb.NewDB(), 4) - chunks3 := make(chan io.ReadCloser, 100) - go func() { - streamWriter3 := snapshots.NewStreamWriter(chunks3) - require.NotNil(t, streamWriter3) - defer streamWriter3.Close() - err := source.Snapshot(version, streamWriter3) - require.NoError(t, err) - }() - streamReader3, err := snapshots.NewStreamReader(chunks3) - require.NoError(t, err) - _, err = target3.Restore(version, snapshottypes.CurrentFormat, streamReader3) - require.Error(t, err) -} - -func BenchmarkMultistoreSnapshot100K(b *testing.B) { - benchmarkMultistoreSnapshot(b, 10, 10000) -} - -func BenchmarkMultistoreSnapshot1M(b *testing.B) { - benchmarkMultistoreSnapshot(b, 10, 100000) -} - -func BenchmarkMultistoreSnapshotRestore100K(b *testing.B) { - benchmarkMultistoreSnapshotRestore(b, 10, 10000) -} - -func BenchmarkMultistoreSnapshotRestore1M(b *testing.B) { - benchmarkMultistoreSnapshotRestore(b, 10, 100000) -} - -func benchmarkMultistoreSnapshot(b *testing.B, stores int, storeKeys uint64) { - b.Skip("Noisy with slow setup time, please see https://github.com/cosmos/cosmos-sdk/issues/8855.") - - b.ReportAllocs() - b.StopTimer() - source := newMultiStoreWithGeneratedData(nil, memdb.NewDB(), stores, storeKeys) - - version := source.LastCommitID().Version - require.EqualValues(b, 1, version) - b.StartTimer() - - for i := 0; i < b.N; i++ { - target := newMultiStore(nil, memdb.NewDB(), stores) - require.EqualValues(b, 0, target.LastCommitID().Version) - - chunks := make(chan io.ReadCloser) - go func() { - streamWriter := snapshots.NewStreamWriter(chunks) - require.NotNil(b, streamWriter) - err := source.Snapshot(uint64(version), streamWriter) - require.NoError(b, err) - }() - for reader := range chunks { - _, err := io.Copy(io.Discard, reader) - require.NoError(b, err) - err = reader.Close() - require.NoError(b, err) - } - } -} - -func benchmarkMultistoreSnapshotRestore(b *testing.B, stores int, storeKeys uint64) { - b.Skip("Noisy with slow setup time, please see https://github.com/cosmos/cosmos-sdk/issues/8855.") - - b.ReportAllocs() - b.StopTimer() - source := newMultiStoreWithGeneratedData(nil, memdb.NewDB(), stores, storeKeys) - version := uint64(source.LastCommitID().Version) - require.EqualValues(b, 1, version) - b.StartTimer() - - for i := 0; i < b.N; i++ { - target := newMultiStore(nil, memdb.NewDB(), stores) - require.EqualValues(b, 0, target.LastCommitID().Version) - - chunks := make(chan io.ReadCloser) - go func() { - writer := snapshots.NewStreamWriter(chunks) - require.NotNil(b, writer) - err := source.Snapshot(version, writer) - require.NoError(b, err) - }() - - reader, err := snapshots.NewStreamReader(chunks) - require.NoError(b, err) - _, err = target.Restore(version, snapshottypes.CurrentFormat, reader) - require.NoError(b, err) - require.Equal(b, source.LastCommitID(), target.LastCommitID()) - } -} diff --git a/store/v2alpha1/multi/store.go b/store/v2alpha1/multi/store.go deleted file mode 100644 index 4916adea6c81..000000000000 --- a/store/v2alpha1/multi/store.go +++ /dev/null @@ -1,915 +0,0 @@ -package multi - -import ( - "errors" - "fmt" - "io" - "math" - "strings" - "sync" - - abci "github.com/tendermint/tendermint/abci/types" - - dbm "github.com/cosmos/cosmos-sdk/db" - prefixdb "github.com/cosmos/cosmos-sdk/db/prefix" - util "github.com/cosmos/cosmos-sdk/internal" - pruningtypes "github.com/cosmos/cosmos-sdk/pruning/types" - sdkmaps "github.com/cosmos/cosmos-sdk/store/internal/maps" - "github.com/cosmos/cosmos-sdk/store/listenkv" - "github.com/cosmos/cosmos-sdk/store/prefix" - "github.com/cosmos/cosmos-sdk/store/tracekv" - types "github.com/cosmos/cosmos-sdk/store/v2alpha1" - "github.com/cosmos/cosmos-sdk/store/v2alpha1/mem" - "github.com/cosmos/cosmos-sdk/store/v2alpha1/smt" - "github.com/cosmos/cosmos-sdk/store/v2alpha1/transient" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/cosmos-sdk/types/kv" -) - -var ( - _ types.Queryable = (*Store)(nil) - _ types.CommitMultiStore = (*Store)(nil) - _ types.CacheMultiStore = (*cacheStore)(nil) - _ types.BasicMultiStore = (*viewStore)(nil) - _ types.KVStore = (*substore)(nil) -) - -var ( - // Root prefixes - merkleRootKey = []byte{0} // Key for root hash of namespace tree - schemaPrefix = []byte{1} // Prefix for store keys (namespaces) - contentPrefix = []byte{2} // Prefix for store contents - - // Per-substore prefixes - substoreMerkleRootKey = []byte{0} // Key for root hashes of Merkle trees - dataPrefix = []byte{1} // Prefix for state mappings - indexPrefix = []byte{2} // Prefix for Store reverse index - smtPrefix = []byte{3} // Prefix for SMT data - - ErrVersionDoesNotExist = errors.New("version does not exist") - ErrMaximumHeight = errors.New("maximum block height reached") -) - -func ErrStoreNotFound(skey string) error { - return fmt.Errorf("store does not exist for key: %s", skey) -} - -// StoreConfig is used to define a schema and other options and pass them to the MultiStore constructor. -type StoreConfig struct { - // Version pruning options for backing DBs. - Pruning pruningtypes.PruningOptions - // The minimum allowed version number. - InitialVersion uint64 - // The backing DB to use for the state commitment Merkle tree data. - // If nil, Merkle data is stored in the state storage DB under a separate prefix. - StateCommitmentDB dbm.DBConnection - - prefixRegistry - PersistentCache types.MultiStorePersistentCache - Upgrades []types.StoreUpgrades - - *traceListenMixin -} - -// StoreSchema defineds a mapping of substore keys to store types -type StoreSchema map[string]types.StoreType - -// Store is the main persistent store type implementing CommitMultiStore. -// Substores consist of an SMT-based state commitment store and state storage. -// Substores must be reserved in the StoreConfig or defined as part of a StoreUpgrade in order to be valid. -// Note: -// The state commitment data and proof are structured in the same basic pattern as the MultiStore, but use an SMT rather than IAVL tree: -// * The state commitment store of each substore consists of a independent SMT. -// * The state commitment of the root store consists of a Merkle map of all registered persistent substore names to the root hash of their corresponding SMTs -type Store struct { - stateDB dbm.DBConnection - stateTxn dbm.DBReadWriter - StateCommitmentDB dbm.DBConnection - stateCommitmentTxn dbm.DBReadWriter - - schema StoreSchema - mem *mem.Store - tran *transient.Store - mtx sync.RWMutex - - // Copied from StoreConfig - Pruning pruningtypes.PruningOptions - InitialVersion uint64 // if - *traceListenMixin - - PersistentCache types.MultiStorePersistentCache - substoreCache map[string]*substore -} - -type substore struct { - root *Store - name string - dataBucket dbm.DBReadWriter - indexBucket dbm.DBReadWriter - stateCommitmentStore *smt.Store -} - -// Branched state -type cacheStore struct { - source types.BasicMultiStore - substores map[string]types.CacheKVStore - *traceListenMixin -} - -// Read-only store for querying past versions -type viewStore struct { - stateView dbm.DBReader - stateCommitmentView dbm.DBReader - substoreCache map[string]*viewSubstore - schema StoreSchema -} - -type viewSubstore struct { - root *viewStore - name string - dataBucket dbm.DBReader - indexBucket dbm.DBReader - stateCommitmentStore *smt.Store -} - -// Builder type used to create a valid schema with no prefix conflicts -type prefixRegistry struct { - StoreSchema - reserved []string -} - -// Mixin type that to compose trace & listen state into each root store variant type -type traceListenMixin struct { - listeners map[string][]types.WriteListener - TraceWriter io.Writer - TraceContext types.TraceContext -} - -func newTraceListenMixin() *traceListenMixin { - return &traceListenMixin{listeners: map[string][]types.WriteListener{}} -} - -// DefaultStoreConfig returns a MultiStore config with an empty schema, a single backing DB, -// pruning with PruneDefault, no listeners and no tracer. -func DefaultStoreConfig() StoreConfig { - return StoreConfig{ - Pruning: pruningtypes.NewPruningOptions(pruningtypes.PruningDefault), - prefixRegistry: prefixRegistry{ - StoreSchema: StoreSchema{}, - }, - traceListenMixin: newTraceListenMixin(), - } -} - -// Returns true for valid store types for a MultiStore schema -func validSubStoreType(sst types.StoreType) bool { - switch sst { - case types.StoreTypePersistent: - return true - case types.StoreTypeMemory: - return true - case types.StoreTypeTransient: - return true - default: - return false - } -} - -// Returns true iff both schema maps match exactly (including mem/tran stores) -func (ss StoreSchema) equal(that StoreSchema) bool { - if len(ss) != len(that) { - return false - } - for key, val := range that { - myval, has := ss[key] - if !has { - return false - } - if val != myval { - return false - } - } - return true -} - -// Parses a schema from the DB -func readSavedSchema(bucket dbm.DBReader) (*prefixRegistry, error) { - ret := prefixRegistry{StoreSchema: StoreSchema{}} - it, err := bucket.Iterator(nil, nil) - if err != nil { - return nil, err - } - for it.Next() { - value := it.Value() - if len(value) != 1 || !validSubStoreType(types.StoreType(value[0])) { - return nil, fmt.Errorf("invalid mapping for store key: %v => %v", it.Key(), value) - } - ret.StoreSchema[string(it.Key())] = types.StoreType(value[0]) - ret.reserved = append(ret.reserved, string(it.Key())) // assume iter yields keys sorted - } - if err = it.Close(); err != nil { - return nil, err - } - return &ret, nil -} - -// NewStore constructs a MultiStore directly from a database. -// Creates a new store if no data exists; otherwise loads existing data. -func NewStore(db dbm.DBConnection, opts StoreConfig) (ret *Store, err error) { - versions, err := db.Versions() - if err != nil { - return - } - // If the DB is not empty, attempt to load existing data - if saved := versions.Count(); saved != 0 { - if opts.InitialVersion != 0 && versions.Last() < opts.InitialVersion { - return nil, fmt.Errorf("latest saved version is less than initial version: %v < %v", - versions.Last(), opts.InitialVersion) - } - } - // To abide by atomicity constraints, revert the DB to the last saved version, in case it contains - // committed data in the "working" version. - // This should only happen if Store.Commit previously failed. - err = db.Revert() - if err != nil { - return - } - stateTxn := db.ReadWriter() - defer func() { - if err != nil { - err = util.CombineErrors(err, stateTxn.Discard(), "stateTxn.Discard also failed") - } - }() - stateCommitmentTxn := stateTxn - if opts.StateCommitmentDB != nil { - var scVersions dbm.VersionSet - scVersions, err = opts.StateCommitmentDB.Versions() - if err != nil { - return - } - // Version sets of each DB must match - if !versions.Equal(scVersions) { - err = fmt.Errorf("different version history between Storage and StateCommitment DB ") - return - } - err = opts.StateCommitmentDB.Revert() - if err != nil { - return - } - stateCommitmentTxn = opts.StateCommitmentDB.ReadWriter() - } - - ret = &Store{ - stateDB: db, - stateTxn: stateTxn, - StateCommitmentDB: opts.StateCommitmentDB, - stateCommitmentTxn: stateCommitmentTxn, - mem: mem.NewStore(), - tran: transient.NewStore(), - - substoreCache: map[string]*substore{}, - - traceListenMixin: opts.traceListenMixin, - PersistentCache: opts.PersistentCache, - - Pruning: opts.Pruning, - InitialVersion: opts.InitialVersion, - } - - // Now load the substore schema - schemaView := prefixdb.NewPrefixReader(ret.stateDB.Reader(), schemaPrefix) - defer func() { - if err != nil { - err = util.CombineErrors(err, schemaView.Discard(), "schemaView.Discard also failed") - err = util.CombineErrors(err, ret.Close(), "base.Close also failed") - } - }() - reg, err := readSavedSchema(schemaView) - if err != nil { - return - } - // If the loaded schema is empty (for new store), just copy the config schema; - // Otherwise, verify it is identical to the config schema - if len(reg.StoreSchema) == 0 { - for k, v := range opts.StoreSchema { - reg.StoreSchema[k] = v - } - reg.reserved = make([]string, len(opts.reserved)) - copy(reg.reserved, opts.reserved) - } else if !reg.equal(opts.StoreSchema) { - - err = errors.New("loaded schema does not match configured schema") - return - - } - // Apply migrations, then clear old schema and write the new one - for _, upgrades := range opts.Upgrades { - err = reg.migrate(ret, upgrades) - if err != nil { - return - } - } - schemaWriter := prefixdb.NewPrefixWriter(ret.stateTxn, schemaPrefix) - it, err := schemaView.Iterator(nil, nil) - if err != nil { - return - } - for it.Next() { - err = schemaWriter.Delete(it.Key()) - if err != nil { - return - } - } - err = it.Close() - if err != nil { - return - } - err = schemaView.Discard() - if err != nil { - return - } - // NB. the migrated contents and schema are not committed until the next store.Commit - for skey, typ := range reg.StoreSchema { - err = schemaWriter.Set([]byte(skey), []byte{byte(typ)}) - if err != nil { - return - } - } - ret.schema = reg.StoreSchema - return ret, nil -} - -func (s *Store) Close() error { - err := s.stateTxn.Discard() - if s.StateCommitmentDB != nil { - err = util.CombineErrors(err, s.stateCommitmentTxn.Discard(), "stateCommitmentTxn.Discard also failed") - } - return err -} - -// Applies store upgrades to the DB contents. -func (pr *prefixRegistry) migrate(store *Store, upgrades types.StoreUpgrades) error { - // Get a view of current state to allow mutation while iterating - reader := store.stateDB.Reader() - scReader := reader - if store.StateCommitmentDB != nil { - scReader = store.StateCommitmentDB.Reader() - } - - for _, key := range upgrades.Deleted { - sst, ix, err := pr.storeInfo(key) - if err != nil { - return err - } - if sst != types.StoreTypePersistent { - return fmt.Errorf("prefix is for non-persistent substore: %v (%v)", key, sst) - } - pr.reserved = append(pr.reserved[:ix], pr.reserved[ix+1:]...) - delete(pr.StoreSchema, key) - - pfx := substorePrefix(key) - subReader := prefixdb.NewPrefixReader(reader, pfx) - it, err := subReader.Iterator(nil, nil) - if err != nil { - return err - } - for it.Next() { - store.stateTxn.Delete(it.Key()) - } - it.Close() - if store.StateCommitmentDB != nil { - subReader = prefixdb.NewPrefixReader(scReader, pfx) - it, err = subReader.Iterator(nil, nil) - if err != nil { - return err - } - for it.Next() { - store.stateCommitmentTxn.Delete(it.Key()) - } - it.Close() - } - } - for _, rename := range upgrades.Renamed { - sst, ix, err := pr.storeInfo(rename.OldKey) - if err != nil { - return err - } - if sst != types.StoreTypePersistent { - return fmt.Errorf("prefix is for non-persistent substore: %v (%v)", rename.OldKey, sst) - } - pr.reserved = append(pr.reserved[:ix], pr.reserved[ix+1:]...) - delete(pr.StoreSchema, rename.OldKey) - err = pr.RegisterSubstore(rename.NewKey, types.StoreTypePersistent) - if err != nil { - return err - } - - oldPrefix := substorePrefix(rename.OldKey) - newPrefix := substorePrefix(rename.NewKey) - subReader := prefixdb.NewPrefixReader(reader, oldPrefix) - subWriter := prefixdb.NewPrefixWriter(store.stateTxn, newPrefix) - it, err := subReader.Iterator(nil, nil) - if err != nil { - return err - } - for it.Next() { - subWriter.Set(it.Key(), it.Value()) - } - it.Close() - if store.StateCommitmentDB != nil { - subReader = prefixdb.NewPrefixReader(scReader, oldPrefix) - subWriter = prefixdb.NewPrefixWriter(store.stateCommitmentTxn, newPrefix) - it, err = subReader.Iterator(nil, nil) - if err != nil { - return err - } - for it.Next() { - subWriter.Set(it.Key(), it.Value()) - } - it.Close() - } - } - - for _, key := range upgrades.Added { - err := pr.RegisterSubstore(key, types.StoreTypePersistent) - if err != nil { - return err - } - } - return nil -} - -func substorePrefix(key string) []byte { - return append(contentPrefix, key...) -} - -// GetKVStore implements BasicMultiStore. -func (rs *Store) GetKVStore(skey types.StoreKey) types.KVStore { - key := skey.Name() - var parent types.KVStore - typ, has := rs.schema[key] - if !has { - panic(ErrStoreNotFound(key)) - } - switch typ { - case types.StoreTypeMemory: - parent = rs.mem - case types.StoreTypeTransient: - parent = rs.tran - case types.StoreTypePersistent: - default: - panic(fmt.Errorf("StoreType not supported: %v", typ)) // should never happen - } - var ret types.KVStore - if parent != nil { // store is non-persistent - ret = prefix.NewStore(parent, []byte(key)) - } else { // store is persistent - sub, err := rs.getSubstore(key) - if err != nil { - panic(err) - } - rs.substoreCache[key] = sub - ret = sub - } - // Wrap with trace/listen if needed. Note: we don't cache this, so users must get a new substore after - // modifying tracers/listeners. - return rs.wrapTraceListen(ret, skey) -} - -// Gets a persistent substore. This reads, but does not update the substore cache. -// Use it in cases where we need to access a store internally (e.g. read/write Merkle keys, queries) -func (rs *Store) getSubstore(key string) (*substore, error) { - if cached, has := rs.substoreCache[key]; has { - return cached, nil - } - pfx := substorePrefix(key) - stateRW := prefixdb.NewPrefixReadWriter(rs.stateTxn, pfx) - stateCommitmentRW := prefixdb.NewPrefixReadWriter(rs.stateCommitmentTxn, pfx) - var stateCommitmentStore *smt.Store - - rootHash, err := stateRW.Get(substoreMerkleRootKey) - if err != nil { - return nil, err - } - if rootHash != nil { - stateCommitmentStore = loadSMT(stateCommitmentRW, rootHash) - } else { - smtdb := prefixdb.NewPrefixReadWriter(stateCommitmentRW, smtPrefix) - stateCommitmentStore = smt.NewStore(smtdb) - } - - return &substore{ - root: rs, - name: key, - dataBucket: prefixdb.NewPrefixReadWriter(stateRW, dataPrefix), - indexBucket: prefixdb.NewPrefixReadWriter(stateRW, indexPrefix), - stateCommitmentStore: stateCommitmentStore, - }, nil -} - -// Resets a substore's state after commit (because root stateTxn has been discarded) -func (s *substore) refresh(rootHash []byte) { - pfx := substorePrefix(s.name) - stateRW := prefixdb.NewPrefixReadWriter(s.root.stateTxn, pfx) - stateCommitmentRW := prefixdb.NewPrefixReadWriter(s.root.stateCommitmentTxn, pfx) - s.dataBucket = prefixdb.NewPrefixReadWriter(stateRW, dataPrefix) - s.indexBucket = prefixdb.NewPrefixReadWriter(stateRW, indexPrefix) - s.stateCommitmentStore = loadSMT(stateCommitmentRW, rootHash) -} - -// Commit implements Committer. -func (s *Store) Commit() types.CommitID { - // Substores read-lock this mutex; lock to prevent racey invalidation of underlying txns - s.mtx.Lock() - defer s.mtx.Unlock() - // Determine the target version - versions, err := s.stateDB.Versions() - if err != nil { - panic(err) - } - target := versions.Last() + 1 - if target > math.MaxInt64 { - panic(ErrMaximumHeight) - } - // Fast forward to initial version if needed - if s.InitialVersion != 0 && target < s.InitialVersion { - target = s.InitialVersion - } - cid, err := s.commit(target) - if err != nil { - panic(err) - } - - // Prune if necessary - previous := cid.Version - 1 - if s.Pruning.Interval != 0 && cid.Version%int64(s.Pruning.Interval) == 0 { - // The range of newly prunable versions - lastPrunable := previous - int64(s.Pruning.KeepRecent) - firstPrunable := lastPrunable - int64(s.Pruning.Interval) - - for version := firstPrunable; version <= lastPrunable; version++ { - s.stateDB.DeleteVersion(uint64(version)) - - if s.StateCommitmentDB != nil { - s.StateCommitmentDB.DeleteVersion(uint64(version)) - } - } - } - - s.tran.Commit() - return *cid -} - -func (s *Store) getMerkleRoots() (ret map[string][]byte, err error) { - ret = map[string][]byte{} - for key := range s.schema { - sub, has := s.substoreCache[key] - if !has { - sub, err = s.getSubstore(key) - if err != nil { - return - } - } - ret[key] = sub.stateCommitmentStore.Root() - } - return -} - -// Calculates root hashes and commits to DB. Does not verify target version or perform pruning. -func (s *Store) commit(target uint64) (id *types.CommitID, err error) { - storeHashes, err := s.getMerkleRoots() - if err != nil { - return - } - // Update substore Merkle roots - for key, storeHash := range storeHashes { - pfx := substorePrefix(key) - stateW := prefixdb.NewPrefixReadWriter(s.stateTxn, pfx) - if err = stateW.Set(substoreMerkleRootKey, storeHash); err != nil { - return - } - } - rootHash := sdkmaps.HashFromMap(storeHashes) - if err = s.stateTxn.Set(merkleRootKey, rootHash); err != nil { - return - } - if err = s.stateTxn.Commit(); err != nil { - return - } - defer func() { - if err != nil { - err = util.CombineErrors(err, s.stateDB.Revert(), "stateDB.Revert also failed") - } - }() - err = s.stateDB.SaveVersion(target) - if err != nil { - return - } - - stateTxn := s.stateDB.ReadWriter() - defer func() { - if err != nil { - err = util.CombineErrors(err, stateTxn.Discard(), "stateTxn.Discard also failed") - } - }() - stateCommitmentTxn := stateTxn - - // If DBs are not separate, StateCommitment state has been committed & snapshotted - if s.StateCommitmentDB != nil { - // if any error is encountered henceforth, we must revert the state and SC dbs - defer func() { - if err != nil { - if delerr := s.stateDB.DeleteVersion(target); delerr != nil { - err = fmt.Errorf("%w: commit rollback failed: %v", err, delerr) - } - } - }() - - err = s.stateCommitmentTxn.Commit() - if err != nil { - return - } - defer func() { - if err != nil { - err = util.CombineErrors(err, s.StateCommitmentDB.Revert(), "stateCommitmentDB.Revert also failed") - } - }() - - err = s.StateCommitmentDB.SaveVersion(target) - if err != nil { - return - } - stateCommitmentTxn = s.StateCommitmentDB.ReadWriter() - } - - s.stateTxn = stateTxn - s.stateCommitmentTxn = stateCommitmentTxn - // the state of all live substores must be refreshed - for key, sub := range s.substoreCache { - sub.refresh(storeHashes[key]) - } - - return &types.CommitID{Version: int64(target), Hash: rootHash}, nil -} - -// LastCommitID implements Committer. -func (s *Store) LastCommitID() types.CommitID { - versions, err := s.stateDB.Versions() - if err != nil { - panic(err) - } - last := versions.Last() - if last == 0 { - return types.CommitID{} - } - // Latest Merkle root is the one currently stored - hash, err := s.stateTxn.Get(merkleRootKey) - if err != nil { - panic(err) - } - return types.CommitID{Version: int64(last), Hash: hash} -} - -// SetInitialVersion implements CommitMultiStore. -func (rs *Store) SetInitialVersion(version uint64) error { - rs.InitialVersion = version - return nil -} - -// GetVersion implements CommitMultiStore. -func (rs *Store) GetVersion(version int64) (types.BasicMultiStore, error) { - return rs.getView(version) -} - -// CacheMultiStore implements BasicMultiStore. -func (rs *Store) CacheMultiStore() types.CacheMultiStore { - return &cacheStore{ - source: rs, - substores: map[string]types.CacheKVStore{}, - traceListenMixin: newTraceListenMixin(), - } -} - -// PruneSnapshotHeight prunes the given height according to the prune strategy. -// If PruneNothing, this is a no-op. -// If other strategy, this height is persisted until it is -// less than - KeepRecent and % Interval == 0 -func (rs *Store) PruneSnapshotHeight(height int64) { - panic("not implemented") -} - -// SetSnapshotInterval sets the interval at which the snapshots are taken. -// It is used by the store to determine which heights to retain until after the snapshot is complete. -func (rs *Store) SetSnapshotInterval(snapshotInterval uint64) { - panic("not implemented") -} - -// parsePath expects a format like /[/] -// Must start with /, subpath may be empty -// Returns error if it doesn't start with / -func parsePath(path string) (storeName string, subpath string, err error) { - if !strings.HasPrefix(path, "/") { - return storeName, subpath, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid path: %s", path) - } - - paths := strings.SplitN(path[1:], "/", 2) - storeName = paths[0] - - if len(paths) == 2 { - subpath = "/" + paths[1] - } - - return storeName, subpath, nil -} - -// Query implements ABCI interface, allows queries. -// -// by default we will return from (latest height -1), -// as we will have merkle proofs immediately (header height = data height + 1) -// If latest-1 is not present, use latest (which must be present) -// if you care to have the latest data to see a tx results, you must -// explicitly set the height you want to see -func (rs *Store) Query(req abci.RequestQuery) (res abci.ResponseQuery) { - if len(req.Data) == 0 { - return sdkerrors.QueryResult(sdkerrors.Wrap(sdkerrors.ErrTxDecode, "query cannot be zero length"), false) - } - - // if height is 0, use the latest height - height := req.Height - if height == 0 { - versions, err := rs.stateDB.Versions() - if err != nil { - return sdkerrors.QueryResult(errors.New("failed to get version info"), false) - } - latest := versions.Last() - if versions.Exists(latest - 1) { - height = int64(latest - 1) - } else { - height = int64(latest) - } - } - if height < 0 { - return sdkerrors.QueryResult(fmt.Errorf("height overflow: %v", height), false) - } - res.Height = height - - storeName, subpath, err := parsePath(req.Path) - if err != nil { - return sdkerrors.QueryResult(sdkerrors.Wrapf(err, "failed to parse path"), false) - } - view, err := rs.getView(height) - if err != nil { - if errors.Is(err, dbm.ErrVersionDoesNotExist) { - err = sdkerrors.ErrInvalidHeight - } - return sdkerrors.QueryResult(sdkerrors.Wrapf(err, "failed to access height"), false) - } - - if _, has := rs.schema[storeName]; !has { - return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "no such store: %s", storeName), false) - } - substore, err := view.getSubstore(storeName) - if err != nil { - return sdkerrors.QueryResult(sdkerrors.Wrapf(err, "failed to access store: %s", storeName), false) - } - - switch subpath { - case "/key": - var err error - res.Key = req.Data // data holds the key bytes - res.Value = substore.Get(res.Key) - if !req.Prove { - break - } - // TODO: actual IBC compatible proof. This is a placeholder so unit tests can pass - res.ProofOps, err = substore.GetProof(res.Key) - if err != nil { - return sdkerrors.QueryResult(fmt.Errorf("merkle proof creation failed for key: %v", res.Key), false) - } - - case "/subspace": - res.Key = req.Data // data holds the subspace prefix - - pairs := kv.Pairs{ - Pairs: make([]kv.Pair, 0), - } - - res.Key = req.Data // data holds the subspace prefix - - iterator := substore.Iterator(res.Key, types.PrefixEndBytes(res.Key)) - for ; iterator.Valid(); iterator.Next() { - pairs.Pairs = append(pairs.Pairs, kv.Pair{Key: iterator.Key(), Value: iterator.Value()}) - } - iterator.Close() - - bz, err := pairs.Marshal() - if err != nil { - panic(fmt.Errorf("failed to marshal KV pairs: %w", err)) - } - - res.Value = bz - - default: - return sdkerrors.QueryResult(sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unexpected query path: %v", req.Path), false) - } - - return res -} - -func loadSMT(stateCommitmentTxn dbm.DBReadWriter, root []byte) *smt.Store { - smtdb := prefixdb.NewPrefixReadWriter(stateCommitmentTxn, smtPrefix) - return smt.LoadStore(smtdb, root) -} - -// Returns closest index and whether it's a match -func binarySearch(hay []string, ndl string) (int, bool) { - var mid int - from, to := 0, len(hay)-1 - for from <= to { - mid = (from + to) / 2 - switch strings.Compare(hay[mid], ndl) { - case -1: - from = mid + 1 - case 1: - to = mid - 1 - default: - return mid, true - } - } - return from, false -} - -func (pr *prefixRegistry) storeInfo(key string) (sst types.StoreType, ix int, err error) { - ix, has := binarySearch(pr.reserved, key) - if !has { - err = fmt.Errorf("prefix does not exist: %v", key) - return - } - sst, has = pr.StoreSchema[key] - if !has { - err = fmt.Errorf("prefix is registered but not in schema: %v", key) - } - - return -} - -func (pr *prefixRegistry) RegisterSubstore(key string, typ types.StoreType) error { - if !validSubStoreType(typ) { - return fmt.Errorf("StoreType not supported: %v", typ) - } - - // Find the neighboring reserved prefix, and check for duplicates and conflicts - i, has := binarySearch(pr.reserved, key) - if has { - return fmt.Errorf("prefix already exists: %v", key) - } - if i > 0 && strings.HasPrefix(key, pr.reserved[i-1]) { - return fmt.Errorf("prefix conflict: '%v' exists, cannot add '%v'", pr.reserved[i-1], key) - } - if i < len(pr.reserved) && strings.HasPrefix(pr.reserved[i], key) { - return fmt.Errorf("prefix conflict: '%v' exists, cannot add '%v'", pr.reserved[i], key) - } - reserved := pr.reserved[:i] - reserved = append(reserved, key) - pr.reserved = append(reserved, pr.reserved[i:]...) - pr.StoreSchema[key] = typ - return nil -} - -func (tlm *traceListenMixin) AddListeners(skey types.StoreKey, listeners []types.WriteListener) { - key := skey.Name() - tlm.listeners[key] = append(tlm.listeners[key], listeners...) -} - -// ListeningEnabled returns if listening is enabled for a specific KVStore -func (tlm *traceListenMixin) ListeningEnabled(key types.StoreKey) bool { - if ls, has := tlm.listeners[key.Name()]; has { - return len(ls) != 0 - } - return false -} - -func (tlm *traceListenMixin) TracingEnabled() bool { - return tlm.TraceWriter != nil -} - -func (tlm *traceListenMixin) SetTracer(w io.Writer) { - tlm.TraceWriter = w -} - -func (tlm *traceListenMixin) SetTraceContext(tc types.TraceContext) { - tlm.TraceContext = tc -} - -func (tlm *traceListenMixin) wrapTraceListen(store types.KVStore, skey types.StoreKey) types.KVStore { - if tlm.TracingEnabled() { - store = tracekv.NewStore(store, tlm.TraceWriter, tlm.TraceContext) - } - if tlm.ListeningEnabled(skey) { - store = listenkv.NewStore(store, skey, tlm.listeners[skey.Name()]) - } - return store -} - -func (s *Store) GetPruning() pruningtypes.PruningOptions { return s.Pruning } -func (s *Store) SetPruning(po pruningtypes.PruningOptions) { s.Pruning = po } diff --git a/store/v2alpha1/multi/store_test.go b/store/v2alpha1/multi/store_test.go deleted file mode 100644 index de09003518d0..000000000000 --- a/store/v2alpha1/multi/store_test.go +++ /dev/null @@ -1,986 +0,0 @@ -package multi - -import ( - "bytes" - "math" - "testing" - - "github.com/stretchr/testify/require" - - abci "github.com/tendermint/tendermint/abci/types" - - "github.com/cosmos/cosmos-sdk/codec" - codecTypes "github.com/cosmos/cosmos-sdk/codec/types" - dbm "github.com/cosmos/cosmos-sdk/db" - "github.com/cosmos/cosmos-sdk/db/memdb" - pruningtypes "github.com/cosmos/cosmos-sdk/pruning/types" - types "github.com/cosmos/cosmos-sdk/store/v2alpha1" - "github.com/cosmos/cosmos-sdk/types/kv" -) - -var ( - alohaData = map[string]string{ - "hello": "goodbye", - "aloha": "shalom", - } - skey_1 = types.NewKVStoreKey("store1") - skey_2 = types.NewKVStoreKey("store2") - skey_3 = types.NewKVStoreKey("store3") - skey_4 = types.NewKVStoreKey("store4") - skey_1b = types.NewKVStoreKey("store1b") - skey_2b = types.NewKVStoreKey("store2b") - skey_3b = types.NewKVStoreKey("store3b") -) - -func simpleStoreConfig(t *testing.T) StoreConfig { - opts := DefaultStoreConfig() - require.NoError(t, opts.RegisterSubstore(skey_1.Name(), types.StoreTypePersistent)) - return opts -} - -func storeConfig123(t *testing.T) StoreConfig { - opts := DefaultStoreConfig() - opts.Pruning = pruningtypes.NewPruningOptions(pruningtypes.PruningNothing) - require.NoError(t, opts.RegisterSubstore(skey_1.Name(), types.StoreTypePersistent)) - require.NoError(t, opts.RegisterSubstore(skey_2.Name(), types.StoreTypePersistent)) - require.NoError(t, opts.RegisterSubstore(skey_3.Name(), types.StoreTypePersistent)) - return opts -} - -func newSubStoreWithData(t *testing.T, db dbm.DBConnection, storeData map[string]string) (*Store, types.KVStore) { - root, err := NewStore(db, simpleStoreConfig(t)) - require.NoError(t, err) - - store := root.GetKVStore(skey_1) - for k, v := range storeData { - store.Set([]byte(k), []byte(v)) - } - return root, store -} - -func TestGetSetHasDelete(t *testing.T) { - _, store := newSubStoreWithData(t, memdb.NewDB(), alohaData) - key := "hello" - - exists := store.Has([]byte(key)) - require.True(t, exists) - - require.EqualValues(t, []byte(alohaData[key]), store.Get([]byte(key))) - - value2 := "notgoodbye" - store.Set([]byte(key), []byte(value2)) - - require.EqualValues(t, value2, store.Get([]byte(key))) - - store.Delete([]byte(key)) - - exists = store.Has([]byte(key)) - require.False(t, exists) - - require.Panics(t, func() { store.Get(nil) }, "Get(nil key) should panic") - require.Panics(t, func() { store.Get([]byte{}) }, "Get(empty key) should panic") - require.Panics(t, func() { store.Has(nil) }, "Has(nil key) should panic") - require.Panics(t, func() { store.Has([]byte{}) }, "Has(empty key) should panic") - require.Panics(t, func() { store.Set(nil, []byte("value")) }, "Set(nil key) should panic") - require.Panics(t, func() { store.Set([]byte{}, []byte("value")) }, "Set(empty key) should panic") - require.Panics(t, func() { store.Set([]byte("key"), nil) }, "Set(nil value) should panic") - sub := store.(*substore) - sub.indexBucket = rwCrudFails{sub.indexBucket, nil} - require.Panics(t, func() { - store.Set([]byte("key"), []byte("value")) - }, "Set() when index fails should panic") -} - -func TestConstructors(t *testing.T) { - db := memdb.NewDB() - - store, err := NewStore(db, simpleStoreConfig(t)) - require.NoError(t, err) - _ = store.GetKVStore(skey_1) - store.Commit() - require.NoError(t, store.Close()) - - t.Run("fail to load if InitialVersion > lowest existing version", func(t *testing.T) { - opts := StoreConfig{InitialVersion: 5, Pruning: pruningtypes.NewPruningOptions(pruningtypes.PruningNothing)} - store, err = NewStore(db, opts) - require.Error(t, err) - db.Close() - }) - - t.Run("can't load store when db.Versions fails", func(t *testing.T) { - store, err = NewStore(dbVersionsFails{memdb.NewDB()}, DefaultStoreConfig()) - require.Error(t, err) - store, err = NewStore(db, StoreConfig{StateCommitmentDB: dbVersionsFails{memdb.NewDB()}}) - require.Error(t, err) - }) - - db = memdb.NewDB() - merkledb := memdb.NewDB() - w := db.Writer() - t.Run("can't use a DB with open writers", func(t *testing.T) { - store, err = NewStore(db, DefaultStoreConfig()) - require.Error(t, err) - w.Discard() - w = merkledb.Writer() - store, err = NewStore(db, StoreConfig{StateCommitmentDB: merkledb}) - require.Error(t, err) - w.Discard() - }) - - t.Run("can't use DBs with different version history", func(t *testing.T) { - merkledb.SaveNextVersion() - store, err = NewStore(db, StoreConfig{StateCommitmentDB: merkledb}) - require.Error(t, err) - }) - merkledb.Close() - - t.Run("can't load existing store if we can't access root hash", func(t *testing.T) { - store, err = NewStore(db, simpleStoreConfig(t)) - require.NoError(t, err) - store.Commit() - require.NoError(t, store.Close()) - // ...whether because root is misssing - w = db.Writer() - s1RootKey := append(contentPrefix, substorePrefix(skey_1.Name())...) - s1RootKey = append(s1RootKey, merkleRootKey...) - w.Delete(s1RootKey) - w.Commit() - db.SaveNextVersion() - store, err = NewStore(db, DefaultStoreConfig()) - require.Error(t, err) - // ...or, because of an error - store, err = NewStore(dbRWCrudFails{db}, DefaultStoreConfig()) - require.Error(t, err) - }) -} - -func TestIterators(t *testing.T) { - _, store := newSubStoreWithData(t, memdb.NewDB(), map[string]string{ - string([]byte{0x00}): "0", - string([]byte{0x00, 0x00}): "0 0", - string([]byte{0x00, 0x01}): "0 1", - string([]byte{0x00, 0x02}): "0 2", - string([]byte{0x01}): "1", - }) - - testCase := func(t *testing.T, iter types.Iterator, expected []string) { - var i int - for i = 0; iter.Valid(); iter.Next() { - expectedValue := expected[i] - value := iter.Value() - require.EqualValues(t, string(value), expectedValue) - i++ - } - require.Equal(t, len(expected), i) - } - - testCase(t, store.Iterator(nil, nil), - []string{"0", "0 0", "0 1", "0 2", "1"}) - testCase(t, store.Iterator([]byte{0}, nil), - []string{"0", "0 0", "0 1", "0 2", "1"}) - testCase(t, store.Iterator([]byte{0}, []byte{0, 1}), - []string{"0", "0 0"}) - testCase(t, store.Iterator([]byte{0}, []byte{1}), - []string{"0", "0 0", "0 1", "0 2"}) - testCase(t, store.Iterator([]byte{0, 1}, []byte{1}), - []string{"0 1", "0 2"}) - testCase(t, store.Iterator(nil, []byte{1}), - []string{"0", "0 0", "0 1", "0 2"}) - testCase(t, store.Iterator([]byte{0}, []byte{0}), []string{}) // start = end - testCase(t, store.Iterator([]byte{1}, []byte{0}), []string{}) // start > end - - testCase(t, store.ReverseIterator(nil, nil), - []string{"1", "0 2", "0 1", "0 0", "0"}) - testCase(t, store.ReverseIterator([]byte{0}, nil), - []string{"1", "0 2", "0 1", "0 0", "0"}) - testCase(t, store.ReverseIterator([]byte{0}, []byte{0, 1}), - []string{"0 0", "0"}) - testCase(t, store.ReverseIterator([]byte{0}, []byte{1}), - []string{"0 2", "0 1", "0 0", "0"}) - testCase(t, store.ReverseIterator([]byte{0, 1}, []byte{1}), - []string{"0 2", "0 1"}) - testCase(t, store.ReverseIterator(nil, []byte{1}), - []string{"0 2", "0 1", "0 0", "0"}) - testCase(t, store.ReverseIterator([]byte{0}, []byte{0}), []string{}) // start = end - testCase(t, store.ReverseIterator([]byte{1}, []byte{0}), []string{}) // start > end - - testCase(t, types.KVStorePrefixIterator(store, []byte{0}), - []string{"0", "0 0", "0 1", "0 2"}) - testCase(t, types.KVStoreReversePrefixIterator(store, []byte{0}), - []string{"0 2", "0 1", "0 0", "0"}) - - require.Panics(t, func() { store.Iterator([]byte{}, nil) }, "Iterator(empty key) should panic") - require.Panics(t, func() { store.Iterator(nil, []byte{}) }, "Iterator(empty key) should panic") - require.Panics(t, func() { store.ReverseIterator([]byte{}, nil) }, "Iterator(empty key) should panic") - require.Panics(t, func() { store.ReverseIterator(nil, []byte{}) }, "Iterator(empty key) should panic") -} - -func TestCommit(t *testing.T) { - testBasic := func(opts StoreConfig) { - db := memdb.NewDB() - store, err := NewStore(db, opts) - require.NoError(t, err) - require.Zero(t, store.LastCommitID()) - idNew := store.Commit() - - // Adding one record changes the hash - s1 := store.GetKVStore(skey_1) - s1.Set([]byte{0}, []byte{0}) - idOne := store.Commit() - require.Equal(t, idNew.Version+1, idOne.Version) - require.NotEqual(t, idNew.Hash, idOne.Hash) - - // Hash of emptied store is same as new store - s1.Delete([]byte{0}) - idEmptied := store.Commit() - require.Equal(t, idNew.Hash, idEmptied.Hash) - - previd := idOne - for i := byte(1); i < 5; i++ { - s1.Set([]byte{i}, []byte{i}) - id := store.Commit() - lastid := store.LastCommitID() - require.Equal(t, id.Hash, lastid.Hash) - require.Equal(t, id.Version, lastid.Version) - require.NotEqual(t, previd.Hash, id.Hash) - require.NotEqual(t, previd.Version, id.Version) - } - } - basicOpts := simpleStoreConfig(t) - basicOpts.Pruning = pruningtypes.NewPruningOptions(pruningtypes.PruningNothing) - t.Run("sanity tests for Merkle hashing", func(t *testing.T) { - testBasic(basicOpts) - }) - t.Run("sanity tests for Merkle hashing with separate DBs", func(t *testing.T) { - basicOpts.StateCommitmentDB = memdb.NewDB() - testBasic(basicOpts) - }) - - // test that we can recover from a failed commit - testFailedCommit := func(t *testing.T, - store *Store, - db dbm.DBConnection, - opts StoreConfig, - ) { - if db == nil { - db = store.stateDB - } - s1 := store.GetKVStore(skey_1) - s1.Set([]byte{0}, []byte{0}) - require.Panics(t, func() { store.Commit() }) - require.NoError(t, store.Close()) - - // No version should be saved in the backing DB(s) - versions, _ := db.Versions() - require.Equal(t, 0, versions.Count()) - if store.StateCommitmentDB != nil { - versions, _ = store.StateCommitmentDB.Versions() - require.Equal(t, 0, versions.Count()) - } - - // The store should now be reloaded successfully - store, err := NewStore(db, opts) - require.NoError(t, err) - s1 = store.GetKVStore(skey_1) - require.Nil(t, s1.Get([]byte{0})) - require.NoError(t, store.Close()) - } - - opts := simpleStoreConfig(t) - opts.Pruning = pruningtypes.NewPruningOptions(pruningtypes.PruningNothing) - - // Ensure Store's commit is rolled back in each failure case... - t.Run("recover after failed Commit", func(t *testing.T) { - store, err := NewStore(dbRWCommitFails{memdb.NewDB()}, opts) - require.NoError(t, err) - testFailedCommit(t, store, nil, opts) - }) - // If SaveVersion and Revert both fail during Store.Commit, the DB will contain - // committed data that belongs to no version: non-atomic behavior from the Store user's perspective. - // So, that data must be reverted when the store is reloaded. - t.Run("recover after failed SaveVersion and Revert", func(t *testing.T) { - var db dbm.DBConnection - db = dbSaveVersionFails{memdb.NewDB()} - // Revert should succeed in initial NewStore call, but fail during Commit - db = dbRevertFails{db, []bool{false, true}} - store, err := NewStore(db, opts) - require.NoError(t, err) - testFailedCommit(t, store, nil, opts) - }) - // Repeat the above for StateCommitmentDB - t.Run("recover after failed StateCommitmentDB Commit", func(t *testing.T) { - opts.StateCommitmentDB = dbRWCommitFails{memdb.NewDB()} - store, err := NewStore(memdb.NewDB(), opts) - require.NoError(t, err) - testFailedCommit(t, store, nil, opts) - }) - t.Run("recover after failed StateCommitmentDB SaveVersion and Revert", func(t *testing.T) { - var db dbm.DBConnection - db = dbSaveVersionFails{memdb.NewDB()} - db = dbRevertFails{db, []bool{false, true}} - opts.StateCommitmentDB = db - store, err := NewStore(memdb.NewDB(), opts) - require.NoError(t, err) - testFailedCommit(t, store, nil, opts) - }) - - opts = simpleStoreConfig(t) - t.Run("recover after stateDB.Versions error triggers failure", func(t *testing.T) { - db := memdb.NewDB() - store, err := NewStore(db, opts) - require.NoError(t, err) - store.stateDB = dbVersionsFails{store.stateDB} - testFailedCommit(t, store, db, opts) - }) - t.Run("recover after stateTxn.Set error triggers failure", func(t *testing.T) { - store, err := NewStore(memdb.NewDB(), opts) - require.NoError(t, err) - store.stateTxn = rwCrudFails{store.stateTxn, merkleRootKey} - testFailedCommit(t, store, nil, opts) - }) - - t.Run("stateDB.DeleteVersion error triggers failure", func(t *testing.T) { - opts.StateCommitmentDB = memdb.NewDB() - store, err := NewStore(memdb.NewDB(), opts) - require.NoError(t, err) - store.stateCommitmentTxn = rwCommitFails{store.stateCommitmentTxn} - store.stateDB = dbDeleteVersionFails{store.stateDB} - require.Panics(t, func() { store.Commit() }) - }) - t.Run("height overflow triggers failure", func(t *testing.T) { - opts.StateCommitmentDB = nil - opts.InitialVersion = math.MaxInt64 - opts.Pruning = pruningtypes.NewPruningOptions(pruningtypes.PruningNothing) - store, err := NewStore(memdb.NewDB(), opts) - require.NoError(t, err) - require.Equal(t, int64(math.MaxInt64), store.Commit().Version) - require.Panics(t, func() { store.Commit() }) - require.Equal(t, int64(math.MaxInt64), store.LastCommitID().Version) // version history not modified - }) - - t.Run("first commit version matches InitialVersion", func(t *testing.T) { - opts = simpleStoreConfig(t) - opts.InitialVersion = 5 - opts.Pruning = pruningtypes.NewPruningOptions(pruningtypes.PruningNothing) - opts.StateCommitmentDB = memdb.NewDB() - store, err := NewStore(memdb.NewDB(), opts) - require.NoError(t, err) - require.Equal(t, int64(5), store.Commit().Version) - }) - - // test improbable failures to fill out test coverage - opts = simpleStoreConfig(t) - store, err := NewStore(memdb.NewDB(), opts) - require.NoError(t, err) - store.Commit() - store.stateDB = dbVersionsFails{store.stateDB} - require.Panics(t, func() { store.LastCommitID() }) - - opts = simpleStoreConfig(t) - opts.StateCommitmentDB = memdb.NewDB() - store, err = NewStore(memdb.NewDB(), opts) - require.NoError(t, err) - store.Commit() - store.stateTxn = rwCrudFails{store.stateTxn, nil} - require.Panics(t, func() { store.LastCommitID() }) -} - -func sliceToSet(slice []uint64) map[uint64]struct{} { - res := make(map[uint64]struct{}) - for _, x := range slice { - res[x] = struct{}{} - } - return res -} - -func TestPruning(t *testing.T) { - // Save versions up to 10 and verify pruning at final commit - testCases := []struct { - pruningtypes.PruningOptions - kept []uint64 - }{ - {pruningtypes.NewCustomPruningOptions(2, 10), []uint64{8, 9, 10}}, - {pruningtypes.NewCustomPruningOptions(0, 10), []uint64{10}}, - {pruningtypes.NewPruningOptions(pruningtypes.PruningEverything), []uint64{8, 9, 10}}, - {pruningtypes.NewPruningOptions(pruningtypes.PruningNothing), []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, - } - - for tci, tc := range testCases { - dbs := []dbm.DBConnection{memdb.NewDB(), memdb.NewDB()} - opts := simpleStoreConfig(t) - opts.Pruning = tc.PruningOptions - opts.StateCommitmentDB = dbs[1] - store, err := NewStore(dbs[0], opts) - require.NoError(t, err) - - s1 := store.GetKVStore(skey_1) - for i := byte(1); i <= 10; i++ { - s1.Set([]byte{i}, []byte{i}) - cid := store.Commit() - latest := uint64(i) - require.Equal(t, latest, uint64(cid.Version)) - } - - for _, db := range dbs { - versions, err := db.Versions() - require.NoError(t, err) - - kept := sliceToSet(tc.kept) - for v := uint64(1); v <= 10; v++ { - _, has := kept[v] - require.Equal(t, has, versions.Exists(v), "Version = %v; tc #%d", v, tci) - } - } - } - - // Test pruning interval - // Save up to 20th version while checking history at specific version checkpoints - testCheckPoints := map[uint64][]uint64{ - 5: {1, 2, 3, 4, 5}, - 10: {10}, - 15: {10, 11, 12, 13, 14, 15}, - 20: {20}, - } - - db := memdb.NewDB() - opts := simpleStoreConfig(t) - opts.Pruning = pruningtypes.NewCustomPruningOptions(0, 10) - store, err := NewStore(db, opts) - require.NoError(t, err) - - for i := byte(1); i <= 20; i++ { - store.GetKVStore(skey_1).Set([]byte{i}, []byte{i}) - - cid := store.Commit() - latest := uint64(i) - require.Equal(t, latest, uint64(cid.Version)) - - kept, has := testCheckPoints[latest] - if !has { - continue - } - - versions, err := db.Versions() - require.NoError(t, err) - - keptMap := sliceToSet(kept) - for v := uint64(1); v <= latest; v++ { - _, has := keptMap[v] - require.Equal(t, has, versions.Exists(v), "Version = %v; tc #%d", v, i) - } - } -} - -func queryPath(skey types.StoreKey, endp string) string { return "/" + skey.Name() + endp } - -func TestQuery(t *testing.T) { - k1, v1 := []byte("k1"), []byte("v1") - k2, v2 := []byte("k2"), []byte("v2") - v3 := []byte("v3") - - ksub := []byte("k") - KVs0 := kv.Pairs{} - KVs1 := kv.Pairs{ - Pairs: []kv.Pair{ - {Key: k1, Value: v1}, - {Key: k2, Value: v2}, - }, - } - KVs2 := kv.Pairs{ - Pairs: []kv.Pair{ - {Key: k1, Value: v3}, - {Key: k2, Value: v2}, - }, - } - - valExpSubEmpty, err := KVs0.Marshal() - require.NoError(t, err) - - valExpSub1, err := KVs1.Marshal() - require.NoError(t, err) - - valExpSub2, err := KVs2.Marshal() - require.NoError(t, err) - - store, err := NewStore(memdb.NewDB(), simpleStoreConfig(t)) - require.NoError(t, err) - cid := store.Commit() - ver := cid.Version - query := abci.RequestQuery{Path: queryPath(skey_1, "/key"), Data: k1, Height: ver} - querySub := abci.RequestQuery{Path: queryPath(skey_1, "/subspace"), Data: ksub, Height: ver} - queryHeight0 := abci.RequestQuery{Path: queryPath(skey_1, "/key"), Data: k1} - - // query subspace before anything set - qres := store.Query(querySub) - require.True(t, qres.IsOK(), qres.Log) - require.Equal(t, valExpSubEmpty, qres.Value) - - sub := store.GetKVStore(skey_1) - require.NotNil(t, sub) - // set data - sub.Set(k1, v1) - sub.Set(k2, v2) - - t.Run("basic queries", func(t *testing.T) { - // set data without commit, doesn't show up - qres = store.Query(query) - require.True(t, qres.IsOK(), qres.Log) - require.Nil(t, qres.Value) - - // commit it, but still don't see on old version - cid = store.Commit() - qres = store.Query(query) - require.True(t, qres.IsOK(), qres.Log) - require.Nil(t, qres.Value) - - // but yes on the new version - query.Height = cid.Version - qres = store.Query(query) - require.True(t, qres.IsOK(), qres.Log) - require.Equal(t, v1, qres.Value) - // and for the subspace - querySub.Height = cid.Version - qres = store.Query(querySub) - require.True(t, qres.IsOK(), qres.Log) - require.Equal(t, valExpSub1, qres.Value) - - // modify - sub.Set(k1, v3) - cid = store.Commit() - - // query will return old values, as height is fixed - qres = store.Query(query) - require.True(t, qres.IsOK(), qres.Log) - require.Equal(t, v1, qres.Value) - - // update to latest height in the query and we are happy - query.Height = cid.Version - qres = store.Query(query) - require.True(t, qres.IsOK(), qres.Log) - require.Equal(t, v3, qres.Value) - // try other key - query2 := abci.RequestQuery{Path: queryPath(skey_1, "/key"), Data: k2, Height: cid.Version} - qres = store.Query(query2) - require.True(t, qres.IsOK(), qres.Log) - require.Equal(t, v2, qres.Value) - // and for the subspace - querySub.Height = cid.Version - qres = store.Query(querySub) - require.True(t, qres.IsOK(), qres.Log) - require.Equal(t, valExpSub2, qres.Value) - - // default (height 0) will show latest-1 - qres = store.Query(queryHeight0) - require.True(t, qres.IsOK(), qres.Log) - require.Equal(t, v1, qres.Value) - }) - - // querying an empty store will fail - store2, err := NewStore(memdb.NewDB(), simpleStoreConfig(t)) - require.NoError(t, err) - qres = store2.Query(queryHeight0) - require.True(t, qres.IsErr()) - - // default shows latest, if latest-1 does not exist - store2.GetKVStore(skey_1).Set(k1, v1) - store2.Commit() - qres = store2.Query(queryHeight0) - require.True(t, qres.IsOK(), qres.Log) - require.Equal(t, v1, qres.Value) - store2.Close() - - t.Run("failed queries", func(t *testing.T) { - // artificial error cases for coverage (should never happen with prescribed usage) - // ensure that height overflow triggers an error - require.NoError(t, err) - store2.stateDB = dbVersionsIs{store2.stateDB, dbm.NewVersionManager([]uint64{uint64(math.MaxInt64) + 1})} - qres = store2.Query(queryHeight0) - require.True(t, qres.IsErr()) - // failure to access versions triggers an error - store2.stateDB = dbVersionsFails{store.stateDB} - qres = store2.Query(queryHeight0) - require.True(t, qres.IsErr()) - store2.Close() - - // query with a nil or empty key fails - badquery := abci.RequestQuery{Path: queryPath(skey_1, "/key"), Data: []byte{}} - qres = store.Query(badquery) - require.True(t, qres.IsErr()) - badquery.Data = nil - qres = store.Query(badquery) - require.True(t, qres.IsErr()) - // querying an invalid height will fail - badquery = abci.RequestQuery{Path: queryPath(skey_1, "/key"), Data: k1, Height: store.LastCommitID().Version + 1} - qres = store.Query(badquery) - require.True(t, qres.IsErr()) - // or an invalid path - badquery = abci.RequestQuery{Path: queryPath(skey_1, "/badpath"), Data: k1} - qres = store.Query(badquery) - require.True(t, qres.IsErr()) - }) - - t.Run("queries with proof", func(t *testing.T) { - // test that proofs are generated with single and separate DBs - testProve := func() { - queryProve0 := abci.RequestQuery{Path: queryPath(skey_1, "/key"), Data: k1, Prove: true} - qres = store.Query(queryProve0) - require.True(t, qres.IsOK(), qres.Log) - require.Equal(t, v1, qres.Value) - require.NotNil(t, qres.ProofOps) - } - testProve() - store.Close() - - opts := simpleStoreConfig(t) - opts.StateCommitmentDB = memdb.NewDB() - store, err = NewStore(memdb.NewDB(), opts) - require.NoError(t, err) - store.GetKVStore(skey_1).Set(k1, v1) - store.Commit() - testProve() - store.Close() - }) -} - -func TestStoreConfig(t *testing.T) { - opts := DefaultStoreConfig() - // Fail with invalid types - require.Error(t, opts.RegisterSubstore(skey_1.Name(), types.StoreTypeDB)) - require.Error(t, opts.RegisterSubstore(skey_1.Name(), types.StoreTypeSMT)) - // Ensure that no prefix conflicts are allowed - require.NoError(t, opts.RegisterSubstore(skey_1.Name(), types.StoreTypePersistent)) - require.NoError(t, opts.RegisterSubstore(skey_2.Name(), types.StoreTypeMemory)) - require.NoError(t, opts.RegisterSubstore(skey_3b.Name(), types.StoreTypeTransient)) - require.Error(t, opts.RegisterSubstore(skey_1b.Name(), types.StoreTypePersistent)) - require.Error(t, opts.RegisterSubstore(skey_2b.Name(), types.StoreTypePersistent)) - require.Error(t, opts.RegisterSubstore(skey_3.Name(), types.StoreTypePersistent)) -} - -func TestMultiStoreBasic(t *testing.T) { - opts := DefaultStoreConfig() - err := opts.RegisterSubstore(skey_1.Name(), types.StoreTypePersistent) - require.NoError(t, err) - db := memdb.NewDB() - store, err := NewStore(db, opts) - require.NoError(t, err) - - store_1 := store.GetKVStore(skey_1) - require.NotNil(t, store_1) - store_1.Set([]byte{0}, []byte{0}) - val := store_1.Get([]byte{0}) - require.Equal(t, []byte{0}, val) - store_1.Delete([]byte{0}) - val = store_1.Get([]byte{0}) - require.Equal(t, []byte(nil), val) -} - -func TestGetVersion(t *testing.T) { - db := memdb.NewDB() - opts := storeConfig123(t) - store, err := NewStore(db, opts) - require.NoError(t, err) - - cid := store.Commit() - view, err := store.GetVersion(cid.Version) - require.NoError(t, err) - subview := view.GetKVStore(skey_1) - require.NotNil(t, subview) - - // version view should be read-only - require.Panics(t, func() { subview.Set([]byte{1}, []byte{1}) }) - require.Panics(t, func() { subview.Delete([]byte{0}) }) - // nonexistent version shouldn't be accessible - _, err = store.GetVersion(cid.Version + 1) - require.Equal(t, ErrVersionDoesNotExist, err) - - substore := store.GetKVStore(skey_1) - require.NotNil(t, substore) - substore.Set([]byte{0}, []byte{0}) - // setting a value shouldn't affect old version - require.False(t, subview.Has([]byte{0})) - - cid = store.Commit() - view, err = store.GetVersion(cid.Version) - require.NoError(t, err) - subview = view.GetKVStore(skey_1) - require.NotNil(t, subview) - // deleting a value shouldn't affect old version - substore.Delete([]byte{0}) - require.Equal(t, []byte{0}, subview.Get([]byte{0})) -} - -func TestMultiStoreMigration(t *testing.T) { - db := memdb.NewDB() - opts := storeConfig123(t) - store, err := NewStore(db, opts) - require.NoError(t, err) - - // write some data in all stores - k1, v1 := []byte("first"), []byte("store") - s1 := store.GetKVStore(skey_1) - require.NotNil(t, s1) - s1.Set(k1, v1) - - k2, v2 := []byte("second"), []byte("restore") - s2 := store.GetKVStore(skey_2) - require.NotNil(t, s2) - s2.Set(k2, v2) - - k3, v3 := []byte("third"), []byte("dropped") - s3 := store.GetKVStore(skey_3) - require.NotNil(t, s3) - s3.Set(k3, v3) - - k4, v4 := []byte("fourth"), []byte("created") - require.Panics(t, func() { store.GetKVStore(skey_4) }) - - cid := store.Commit() - require.NoError(t, store.Close()) - var migratedID types.CommitID - - // Load without changes and make sure it is sensible - store, err = NewStore(db, opts) - require.NoError(t, err) - - // let's query data to see it was saved properly - s2 = store.GetKVStore(skey_2) - require.NotNil(t, s2) - require.Equal(t, v2, s2.Get(k2)) - require.NoError(t, store.Close()) - - t.Run("basic migration", func(t *testing.T) { - // now, let's load with upgrades... - opts.Upgrades = []types.StoreUpgrades{ - { - Added: []string{skey_4.Name()}, - Renamed: []types.StoreRename{{ - OldKey: skey_2.Name(), - NewKey: skey_2b.Name(), - }}, - Deleted: []string{skey_3.Name()}, - }, - } - store, err = NewStore(db, opts) - require.Nil(t, err) - - // s1 was not changed - s1 = store.GetKVStore(skey_1) - require.NotNil(t, s1) - require.Equal(t, v1, s1.Get(k1)) - - // store2 is no longer valid - require.Panics(t, func() { store.GetKVStore(skey_2) }) - // store2b has the old data - rs2 := store.GetKVStore(skey_2b) - require.NotNil(t, rs2) - require.Equal(t, v2, rs2.Get(k2)) - - // store3 is gone - require.Panics(t, func() { s3 = store.GetKVStore(skey_3) }) - - // store4 is valid - s4 := store.GetKVStore(skey_4) - require.NotNil(t, s4) - values := 0 - it := s4.Iterator(nil, nil) - for ; it.Valid(); it.Next() { - values += 1 - } - require.Zero(t, values) - require.NoError(t, it.Close()) - // write something inside store4 - s4.Set(k4, v4) - - // store this migrated data, and load it again without migrations - migratedID = store.Commit() - require.Equal(t, migratedID.Version, int64(2)) - require.NoError(t, store.Close()) - }) - - t.Run("reload after migrations", func(t *testing.T) { - // fail to load the migrated store with the old schema - store, err = NewStore(db, storeConfig123(t)) - require.Error(t, err) - - // pass in a schema reflecting the migrations - migratedOpts := DefaultStoreConfig() - err = migratedOpts.RegisterSubstore(skey_1.Name(), types.StoreTypePersistent) - require.NoError(t, err) - err = migratedOpts.RegisterSubstore(skey_2b.Name(), types.StoreTypePersistent) - require.NoError(t, err) - err = migratedOpts.RegisterSubstore(skey_4.Name(), types.StoreTypePersistent) - require.NoError(t, err) - store, err = NewStore(db, migratedOpts) - require.Nil(t, err) - require.Equal(t, migratedID, store.LastCommitID()) - - // query this new store - rl1 := store.GetKVStore(skey_1) - require.NotNil(t, rl1) - require.Equal(t, v1, rl1.Get(k1)) - - rl2 := store.GetKVStore(skey_2b) - require.NotNil(t, rl2) - require.Equal(t, v2, rl2.Get(k2)) - - rl4 := store.GetKVStore(skey_4) - require.NotNil(t, rl4) - require.Equal(t, v4, rl4.Get(k4)) - }) - - t.Run("load view from before migrations", func(t *testing.T) { - // load and check a view of the store at first commit - view, err := store.GetVersion(cid.Version) - require.NoError(t, err) - - s1 = view.GetKVStore(skey_1) - require.NotNil(t, s1) - require.Equal(t, v1, s1.Get(k1)) - - s2 = view.GetKVStore(skey_2) - require.NotNil(t, s2) - require.Equal(t, v2, s2.Get(k2)) - - s3 = view.GetKVStore(skey_3) - require.NotNil(t, s3) - require.Equal(t, v3, s3.Get(k3)) - - require.Panics(t, func() { - view.GetKVStore(skey_4) - }) - }) -} - -func TestTrace(t *testing.T) { - key, value := []byte("test-key"), []byte("test-value") - tctx := types.TraceContext(map[string]interface{}{"blockHeight": 64}) - - expected_Set := "{\"operation\":\"write\",\"key\":\"dGVzdC1rZXk=\",\"value\":\"dGVzdC12YWx1ZQ==\",\"metadata\":{\"blockHeight\":64}}\n" - expected_Get := "{\"operation\":\"read\",\"key\":\"dGVzdC1rZXk=\",\"value\":\"dGVzdC12YWx1ZQ==\",\"metadata\":{\"blockHeight\":64}}\n" - expected_Get_missing := "{\"operation\":\"read\",\"key\":\"dGVzdC1rZXk=\",\"value\":\"\",\"metadata\":{\"blockHeight\":64}}\n" - expected_Delete := "{\"operation\":\"delete\",\"key\":\"dGVzdC1rZXk=\",\"value\":\"\",\"metadata\":{\"blockHeight\":64}}\n" - expected_IterKey := "{\"operation\":\"iterKey\",\"key\":\"dGVzdC1rZXk=\",\"value\":\"\",\"metadata\":{\"blockHeight\":64}}\n" - expected_IterValue := "{\"operation\":\"iterValue\",\"key\":\"\",\"value\":\"dGVzdC12YWx1ZQ==\",\"metadata\":{\"blockHeight\":64}}\n" - - db := memdb.NewDB() - opts := simpleStoreConfig(t) - require.NoError(t, opts.RegisterSubstore(skey_2.Name(), types.StoreTypeMemory)) - require.NoError(t, opts.RegisterSubstore(skey_3.Name(), types.StoreTypeTransient)) - - store, err := NewStore(db, opts) - require.NoError(t, err) - store.SetTraceContext(tctx) - require.False(t, store.TracingEnabled()) - - var buf bytes.Buffer - store.SetTracer(&buf) - require.True(t, store.TracingEnabled()) - - for _, skey := range []types.StoreKey{skey_1, skey_2, skey_3} { - buf.Reset() - store.GetKVStore(skey).Get(key) - require.Equal(t, expected_Get_missing, buf.String()) - - buf.Reset() - store.GetKVStore(skey).Set(key, value) - require.Equal(t, expected_Set, buf.String()) - - buf.Reset() - require.Equal(t, value, store.GetKVStore(skey).Get(key)) - require.Equal(t, expected_Get, buf.String()) - - iter := store.GetKVStore(skey).Iterator(nil, nil) - buf.Reset() - require.Equal(t, key, iter.Key()) - require.Equal(t, expected_IterKey, buf.String()) - buf.Reset() - require.Equal(t, value, iter.Value()) - require.Equal(t, expected_IterValue, buf.String()) - require.NoError(t, iter.Close()) - - buf.Reset() - store.GetKVStore(skey).Delete(key) - require.Equal(t, expected_Delete, buf.String()) - - } - store.SetTracer(nil) - require.False(t, store.TracingEnabled()) - require.NoError(t, store.Close()) -} - -func TestListeners(t *testing.T) { - kvPairs := []types.KVPair{ - {Key: []byte{1}, Value: []byte("v1")}, - {Key: []byte{2}, Value: []byte("v2")}, - {Key: []byte{3}, Value: []byte("v3")}, - } - - testCases := []struct { - key []byte - value []byte - skey types.StoreKey - }{ - { - key: kvPairs[0].Key, - value: kvPairs[0].Value, - skey: skey_1, - }, - { - key: kvPairs[1].Key, - value: kvPairs[1].Value, - skey: skey_2, - }, - { - key: kvPairs[2].Key, - value: kvPairs[2].Value, - skey: skey_3, - }, - } - - interfaceRegistry := codecTypes.NewInterfaceRegistry() - marshaller := codec.NewProtoCodec(interfaceRegistry) - - db := memdb.NewDB() - opts := simpleStoreConfig(t) - require.NoError(t, opts.RegisterSubstore(skey_2.Name(), types.StoreTypeMemory)) - require.NoError(t, opts.RegisterSubstore(skey_3.Name(), types.StoreTypeTransient)) - - store, err := NewStore(db, opts) - require.NoError(t, err) - - for i, tc := range testCases { - var buf bytes.Buffer - listener := types.NewStoreKVPairWriteListener(&buf, marshaller) - store.AddListeners(tc.skey, []types.WriteListener{listener}) - require.True(t, store.ListeningEnabled(tc.skey)) - - // Set case - expected := types.StoreKVPair{ - Key: tc.key, - Value: tc.value, - StoreKey: tc.skey.Name(), - Delete: false, - } - var kvpair types.StoreKVPair - - buf.Reset() - store.GetKVStore(tc.skey).Set(tc.key, tc.value) - require.NoError(t, marshaller.UnmarshalLengthPrefixed(buf.Bytes(), &kvpair)) - require.Equal(t, expected, kvpair, i) - - // Delete case - expected = types.StoreKVPair{ - Key: tc.key, - Value: nil, - StoreKey: tc.skey.Name(), - Delete: true, - } - kvpair = types.StoreKVPair{} - - buf.Reset() - store.GetKVStore(tc.skey).Delete(tc.key) - require.NoError(t, marshaller.UnmarshalLengthPrefixed(buf.Bytes(), &kvpair)) - require.Equal(t, expected, kvpair, i) - } - require.NoError(t, store.Close()) -} diff --git a/store/v2alpha1/multi/sub_store.go b/store/v2alpha1/multi/sub_store.go deleted file mode 100644 index 613b7ac2c412..000000000000 --- a/store/v2alpha1/multi/sub_store.go +++ /dev/null @@ -1,117 +0,0 @@ -package multi - -import ( - "crypto/sha256" - "io" - "sync" - - dbm "github.com/cosmos/cosmos-sdk/db" - dbutil "github.com/cosmos/cosmos-sdk/internal/db" - "github.com/cosmos/cosmos-sdk/store/cachekv" - "github.com/cosmos/cosmos-sdk/store/listenkv" - "github.com/cosmos/cosmos-sdk/store/tracekv" - "github.com/cosmos/cosmos-sdk/store/types" -) - -// Get implements KVStore. -func (s *substore) Get(key []byte) []byte { - s.root.mtx.RLock() - defer s.root.mtx.RUnlock() - - val, err := s.dataBucket.Get(key) - if err != nil { - panic(err) - } - return val -} - -// Has implements KVStore. -func (s *substore) Has(key []byte) bool { - s.root.mtx.RLock() - defer s.root.mtx.RUnlock() - - has, err := s.dataBucket.Has(key) - if err != nil { - panic(err) - } - return has -} - -// Set implements KVStore. -func (s *substore) Set(key, value []byte) { - s.root.mtx.Lock() - defer s.root.mtx.Unlock() - - err := s.dataBucket.Set(key, value) - if err != nil { - panic(err) - } - s.stateCommitmentStore.Set(key, value) - khash := sha256.Sum256(key) - err = s.indexBucket.Set(khash[:], key) - if err != nil { - panic(err) - } -} - -// Delete implements KVStore. -func (s *substore) Delete(key []byte) { - khash := sha256.Sum256(key) - s.root.mtx.Lock() - defer s.root.mtx.Unlock() - - s.stateCommitmentStore.Delete(key) - _ = s.indexBucket.Delete(khash[:]) - _ = s.dataBucket.Delete(key) -} - -type contentsIterator struct { - types.Iterator - locker sync.Locker -} - -func (s *substore) newSubstoreIterator(source dbm.Iterator) *contentsIterator { - locker := s.root.mtx.RLocker() - locker.Lock() - return &contentsIterator{dbutil.DBToStoreIterator(source), locker} -} - -func (it *contentsIterator) Close() error { - defer it.locker.Unlock() - return it.Iterator.Close() -} - -// Iterator implements KVStore. -func (s *substore) Iterator(start, end []byte) types.Iterator { - iter, err := s.dataBucket.Iterator(start, end) - if err != nil { - panic(err) - } - return s.newSubstoreIterator(iter) -} - -// ReverseIterator implements KVStore. -func (s *substore) ReverseIterator(start, end []byte) types.Iterator { - iter, err := s.dataBucket.ReverseIterator(start, end) - if err != nil { - panic(err) - } - return s.newSubstoreIterator(iter) -} - -// GetStoreType implements Store. -func (s *substore) GetStoreType() types.StoreType { - return types.StoreTypePersistent -} - -func (s *substore) CacheWrap() types.CacheWrap { - return cachekv.NewStore(s) -} - -func (s *substore) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { - return cachekv.NewStore(tracekv.NewStore(s, w, tc)) -} - -func (s *substore) CacheWrapWithListeners(storeKey types.StoreKey, listeners []types.WriteListener) types.CacheWrap { - return cachekv.NewStore(listenkv.NewStore(s, storeKey, listeners)) -} diff --git a/store/v2alpha1/multi/test_util.go b/store/v2alpha1/multi/test_util.go deleted file mode 100644 index d7005cd86f0b..000000000000 --- a/store/v2alpha1/multi/test_util.go +++ /dev/null @@ -1,88 +0,0 @@ -//nolint:unused -package multi - -import ( - "bytes" - "errors" - - dbm "github.com/cosmos/cosmos-sdk/db" -) - -type ( - dbDeleteVersionFails struct{ dbm.DBConnection } - dbRWCommitFails struct{ dbm.DBConnection } - dbRWCrudFails struct{ dbm.DBConnection } - dbSaveVersionFails struct{ dbm.DBConnection } - dbRevertFails struct { - dbm.DBConnection - // order of calls to fail on (eg. [1, 0] => first call fails; second succeeds) - failOn []bool - } -) - -type dbVersionsIs struct { - dbm.DBConnection - vset dbm.VersionSet -} -type ( - dbVersionsFails struct{ dbm.DBConnection } - rwCommitFails struct{ dbm.DBReadWriter } - rwCrudFails struct { - dbm.DBReadWriter - onKey []byte - } -) - -func (dbVersionsFails) Versions() (dbm.VersionSet, error) { return nil, errors.New("dbVersionsFails") } -func (db dbVersionsIs) Versions() (dbm.VersionSet, error) { return db.vset, nil } -func (db dbRWCrudFails) ReadWriter() dbm.DBReadWriter { - return rwCrudFails{db.DBConnection.ReadWriter(), nil} -} -func (dbSaveVersionFails) SaveVersion(uint64) error { return errors.New("dbSaveVersionFails") } -func (db dbRevertFails) Revert() error { - fail := false - if len(db.failOn) > 0 { - fail, db.failOn = db.failOn[0], db.failOn[1:] //nolint:staticcheck - } - if fail { - return errors.New("dbRevertFails") - } - return db.DBConnection.Revert() -} -func (dbDeleteVersionFails) DeleteVersion(uint64) error { return errors.New("dbDeleteVersionFails") } -func (tx rwCommitFails) Commit() error { - tx.Discard() - return errors.New("rwCommitFails") -} - -func (db dbRWCommitFails) ReadWriter() dbm.DBReadWriter { - return rwCommitFails{db.DBConnection.ReadWriter()} -} - -func (rw rwCrudFails) Get(k []byte) ([]byte, error) { - if rw.onKey == nil || bytes.Equal(rw.onKey, k) { - return nil, errors.New("rwCrudFails.Get") - } - return rw.DBReadWriter.Get(k) -} - -func (rw rwCrudFails) Has(k []byte) (bool, error) { - if rw.onKey == nil || bytes.Equal(rw.onKey, k) { - return false, errors.New("rwCrudFails.Has") - } - return rw.DBReadWriter.Has(k) -} - -func (rw rwCrudFails) Set(k []byte, v []byte) error { - if rw.onKey == nil || bytes.Equal(rw.onKey, k) { - return errors.New("rwCrudFails.Set") - } - return rw.DBReadWriter.Set(k, v) -} - -func (rw rwCrudFails) Delete(k []byte) error { - if rw.onKey == nil || bytes.Equal(rw.onKey, k) { - return errors.New("rwCrudFails.Delete") - } - return rw.DBReadWriter.Delete(k) -} diff --git a/store/v2alpha1/multi/view_store.go b/store/v2alpha1/multi/view_store.go deleted file mode 100644 index b06596c3161b..000000000000 --- a/store/v2alpha1/multi/view_store.go +++ /dev/null @@ -1,178 +0,0 @@ -package multi - -import ( - "errors" - "io" - - dbm "github.com/cosmos/cosmos-sdk/db" - prefixdb "github.com/cosmos/cosmos-sdk/db/prefix" - util "github.com/cosmos/cosmos-sdk/internal" - dbutil "github.com/cosmos/cosmos-sdk/internal/db" - "github.com/cosmos/cosmos-sdk/store/cachekv" - "github.com/cosmos/cosmos-sdk/store/listenkv" - "github.com/cosmos/cosmos-sdk/store/tracekv" - types "github.com/cosmos/cosmos-sdk/store/v2alpha1" - "github.com/cosmos/cosmos-sdk/store/v2alpha1/smt" -) - -var ErrReadOnly = errors.New("cannot modify read-only store") - -func (s *viewSubstore) GetStateCommitmentStore() *smt.Store { - return s.stateCommitmentStore -} - -// Get implements KVStore. -func (s *viewSubstore) Get(key []byte) []byte { - val, err := s.dataBucket.Get(key) - if err != nil { - panic(err) - } - return val -} - -// Has implements KVStore. -func (s *viewSubstore) Has(key []byte) bool { - has, err := s.dataBucket.Has(key) - if err != nil { - panic(err) - } - return has -} - -// Set implements KVStore. -func (s *viewSubstore) Set(key []byte, value []byte) { - panic(ErrReadOnly) -} - -// Delete implements KVStore. -func (s *viewSubstore) Delete(key []byte) { - panic(ErrReadOnly) -} - -// Iterator implements KVStore. -func (s *viewSubstore) Iterator(start, end []byte) types.Iterator { - iter, err := s.dataBucket.Iterator(start, end) - if err != nil { - panic(err) - } - return dbutil.DBToStoreIterator(iter) -} - -// ReverseIterator implements KVStore. -func (s *viewSubstore) ReverseIterator(start, end []byte) types.Iterator { - iter, err := s.dataBucket.ReverseIterator(start, end) - if err != nil { - panic(err) - } - return dbutil.DBToStoreIterator(iter) -} - -// GetStoreType implements Store. -func (s *viewSubstore) GetStoreType() types.StoreType { - return types.StoreTypePersistent -} - -func (st *viewSubstore) CacheWrap() types.CacheWrap { - return cachekv.NewStore(st) -} - -func (st *viewSubstore) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { - return cachekv.NewStore(tracekv.NewStore(st, w, tc)) -} - -func (st *viewSubstore) CacheWrapWithListeners(storeKey types.StoreKey, listeners []types.WriteListener) types.CacheWrap { - return cachekv.NewStore(listenkv.NewStore(st, storeKey, listeners)) -} - -func (s *viewStore) getMerkleRoots() (ret map[string][]byte, err error) { - ret = map[string][]byte{} - for key := range s.schema { - sub, has := s.substoreCache[key] - if !has { - sub, err = s.getSubstore(key) - if err != nil { - return - } - } - ret[key] = sub.stateCommitmentStore.Root() - } - return -} - -func (store *Store) getView(version int64) (ret *viewStore, err error) { - stateView, err := store.stateDB.ReaderAt(uint64(version)) - if err != nil { - return - } - defer func() { - if err != nil { - err = util.CombineErrors(err, stateView.Discard(), "stateView.Discard also failed") - } - }() - - stateCommitmentView := stateView - if store.StateCommitmentDB != nil { - stateCommitmentView, err = store.StateCommitmentDB.ReaderAt(uint64(version)) - if err != nil { - return - } - defer func() { - if err != nil { - err = util.CombineErrors(err, stateCommitmentView.Discard(), "stateCommitmentView.Discard also failed") - } - }() - } - // Now read this version's schema - schemaView := prefixdb.NewPrefixReader(stateView, schemaPrefix) - defer func() { - if err != nil { - err = util.CombineErrors(err, schemaView.Discard(), "schemaView.Discard also failed") - } - }() - pr, err := readSavedSchema(schemaView) - if err != nil { - return - } - // The migrated contents and schema are not committed until the next store.Commit - ret = &viewStore{ - stateView: stateView, - stateCommitmentView: stateCommitmentView, - substoreCache: map[string]*viewSubstore{}, - schema: pr.StoreSchema, - } - return ret, nil -} - -func (vs *viewStore) GetKVStore(skey types.StoreKey) types.KVStore { - key := skey.Name() - if _, has := vs.schema[key]; !has { - panic(ErrStoreNotFound(key)) - } - ret, err := vs.getSubstore(key) - if err != nil { - panic(err) - } - vs.substoreCache[key] = ret - return ret -} - -// Reads but does not update substore cache -func (vs *viewStore) getSubstore(key string) (*viewSubstore, error) { - if cached, has := vs.substoreCache[key]; has { - return cached, nil - } - pfx := substorePrefix(key) - stateR := prefixdb.NewPrefixReader(vs.stateView, pfx) - stateCommitmentR := prefixdb.NewPrefixReader(vs.stateCommitmentView, pfx) - rootHash, err := stateR.Get(merkleRootKey) - if err != nil { - return nil, err - } - return &viewSubstore{ - root: vs, - name: key, - dataBucket: prefixdb.NewPrefixReader(stateR, dataPrefix), - indexBucket: prefixdb.NewPrefixReader(stateR, indexPrefix), - stateCommitmentStore: loadSMT(dbm.ReaderAsReadWriter(stateCommitmentR), rootHash), - }, nil -} diff --git a/store/v2alpha1/smt/ics23.go b/store/v2alpha1/smt/ics23.go deleted file mode 100644 index 43b75a19788f..000000000000 --- a/store/v2alpha1/smt/ics23.go +++ /dev/null @@ -1,124 +0,0 @@ -// Here we implement proof generation according to the ICS-23 specification: -// https://github.com/cosmos/ibc/tree/master/spec/core/ics-023-vector-commitments - -package smt - -import ( - "crypto/sha256" - "fmt" - - dbm "github.com/cosmos/cosmos-sdk/db" - - ics23 "github.com/confio/ics23/go" -) - -func createIcs23Proof(store *Store, key []byte) (*ics23.CommitmentProof, error) { - ret := &ics23.CommitmentProof{} - path := sha256.Sum256(key) - has, err := store.tree.Has(key) - if err != nil { - return nil, err - } - if has { // Membership proof - value, err := store.values.Get(path[:]) - if err != nil { - return nil, err - } - if value == nil { - return nil, fmt.Errorf("value not found for: %v", key) - } - proof, err := store.tree.Prove(key) - if err != nil { - return nil, err - } - ret.Proof = &ics23.CommitmentProof_Exist{Exist: &ics23.ExistenceProof{ - Key: path[:], - Value: value, - Leaf: ics23.SmtSpec.LeafSpec, - Path: convertInnerOps(path[:], proof.SideNodes), - }} - } else { // Non-membership - nonexist, err := toNonExistenceProof(store, path) - if err != nil { - return nil, err - } - ret.Proof = &ics23.CommitmentProof_Nonexist{Nonexist: nonexist} - } - return ret, nil -} - -func toNonExistenceProof(store *Store, path [32]byte) (*ics23.NonExistenceProof, error) { - // Seek to our neighbors via the backing DB - getNext := func(it dbm.Iterator) (*ics23.ExistenceProof, error) { - defer it.Close() - if it.Next() { - value, err := store.values.Get(it.Key()) - if err != nil { - return nil, err - } - if value == nil { - return nil, fmt.Errorf("value not found for: %v", it.Value()) - } - proof, err := store.tree.Prove(it.Value()) // pass the preimage to Prove - if err != nil { - return nil, err - } - return &ics23.ExistenceProof{ - Key: it.Key(), - Value: value, - Leaf: ics23.SmtSpec.LeafSpec, - Path: convertInnerOps(it.Key(), proof.SideNodes), - }, nil - } - return nil, nil - } - var lproof, rproof *ics23.ExistenceProof - it, err := store.preimages.ReverseIterator(nil, path[:]) - if err != nil { - return nil, err - } - lproof, err = getNext(it) - if err != nil { - return nil, err - } - it, err = store.preimages.Iterator(path[:], nil) - if err != nil { - return nil, err - } - rproof, err = getNext(it) - if err != nil { - return nil, err - } - return &ics23.NonExistenceProof{ - Key: path[:], - Left: lproof, - Right: rproof, - }, nil -} - -func convertInnerOps(path []byte, sideNodes [][]byte) []*ics23.InnerOp { - depth := len(sideNodes) - inners := make([]*ics23.InnerOp, 0, depth) - for i := 0; i < len(sideNodes); i++ { - op := &ics23.InnerOp{ - Hash: ics23.HashOp_SHA256, - Prefix: []byte{1}, - } - if getBitAtFromMSB(path, depth-1-i) == 1 { - // right child is on path - op.Prefix = append(op.Prefix, sideNodes[i]...) - } else { - op.Suffix = sideNodes[i] - } - inners = append(inners, op) - } - return inners -} - -// getBitAtFromMSB gets the bit at an offset from the most significant bit -func getBitAtFromMSB(data []byte, position int) int { - if int(data[position/8])&(1<<(8-1-uint(position)%8)) > 0 { - return 1 - } - return 0 -} diff --git a/store/v2alpha1/smt/ics23_test.go b/store/v2alpha1/smt/ics23_test.go deleted file mode 100644 index a94cc6f0a477..000000000000 --- a/store/v2alpha1/smt/ics23_test.go +++ /dev/null @@ -1,108 +0,0 @@ -package smt_test - -import ( - "crypto/sha256" - "testing" - - ics23 "github.com/confio/ics23/go" - "github.com/stretchr/testify/assert" - - "github.com/cosmos/cosmos-sdk/db/memdb" - store "github.com/cosmos/cosmos-sdk/store/v2alpha1/smt" -) - -func TestProofICS23(t *testing.T) { - txn := memdb.NewDB().ReadWriter() - s := store.NewStore(txn) - // pick keys whose hashes begin with different bits - key00 := []byte("foo") // 00101100 = sha256(foo)[0] - key01 := []byte("bill") // 01100010 - key10 := []byte("baz") // 10111010 - key11 := []byte("bar") // 11111100 - path00 := sha256.Sum256(key00) - path01 := sha256.Sum256(key01) - path10 := sha256.Sum256(key10) - val1 := []byte("0") - val2 := []byte("1") - - s.Set(key01, val1) - - // Membership - proof, err := s.GetProofICS23(key01) - assert.NoError(t, err) - nonexist := proof.GetNonexist() - assert.Nil(t, nonexist) - exist := proof.GetExist() - assert.NotNil(t, exist) - assert.Equal(t, 0, len(exist.Path)) - assert.NoError(t, exist.Verify(ics23.SmtSpec, s.Root(), path01[:], val1)) - - // Non-membership - proof, err = s.GetProofICS23(key00) // When leaf is leftmost node - assert.NoError(t, err) - nonexist = proof.GetNonexist() - assert.NotNil(t, nonexist) - assert.Nil(t, nonexist.Left) - assert.Equal(t, path00[:], nonexist.Key) - assert.NotNil(t, nonexist.Right) - assert.Equal(t, 0, len(nonexist.Right.Path)) - assert.NoError(t, nonexist.Verify(ics23.SmtSpec, s.Root(), path00[:])) - - proof, err = s.GetProofICS23(key10) // When rightmost - assert.NoError(t, err) - nonexist = proof.GetNonexist() - assert.NotNil(t, nonexist) - assert.NotNil(t, nonexist.Left) - assert.Equal(t, 0, len(nonexist.Left.Path)) - assert.Nil(t, nonexist.Right) - assert.NoError(t, nonexist.Verify(ics23.SmtSpec, s.Root(), path10[:])) - badNonexist := nonexist - - s.Set(key11, val2) - - proof, err = s.GetProofICS23(key10) // In between two keys - assert.NoError(t, err) - nonexist = proof.GetNonexist() - assert.NotNil(t, nonexist) - assert.Equal(t, path10[:], nonexist.Key) - assert.NotNil(t, nonexist.Left) - assert.Equal(t, 1, len(nonexist.Left.Path)) - assert.NotNil(t, nonexist.Right) - assert.Equal(t, 1, len(nonexist.Right.Path)) - assert.NoError(t, nonexist.Verify(ics23.SmtSpec, s.Root(), path10[:])) - - // Make sure proofs work with a loaded store - root := s.Root() - s = store.LoadStore(txn, root) - proof, err = s.GetProofICS23(key10) - assert.NoError(t, err) - nonexist = proof.GetNonexist() - assert.Equal(t, path10[:], nonexist.Key) - assert.NotNil(t, nonexist.Left) - assert.Equal(t, 1, len(nonexist.Left.Path)) - assert.NotNil(t, nonexist.Right) - assert.Equal(t, 1, len(nonexist.Right.Path)) - assert.NoError(t, nonexist.Verify(ics23.SmtSpec, s.Root(), path10[:])) - - // Invalid proofs should fail to verify - badExist := exist // expired proof - assert.Error(t, badExist.Verify(ics23.SmtSpec, s.Root(), path01[:], val1)) - - badExist = nonexist.Left - badExist.Key = key01 // .Key must contain key path - assert.Error(t, badExist.Verify(ics23.SmtSpec, s.Root(), path01[:], val1)) - - badExist = nonexist.Left - badExist.Path[0].Prefix = []byte{0} // wrong inner node prefix - assert.Error(t, badExist.Verify(ics23.SmtSpec, s.Root(), path01[:], val1)) - - badExist = nonexist.Left - badExist.Path = []*ics23.InnerOp{} // empty path - assert.Error(t, badExist.Verify(ics23.SmtSpec, s.Root(), path01[:], val1)) - - assert.Error(t, badNonexist.Verify(ics23.SmtSpec, s.Root(), path10[:])) - - badNonexist = nonexist - badNonexist.Key = key10 - assert.Error(t, badNonexist.Verify(ics23.SmtSpec, s.Root(), path10[:])) -} diff --git a/store/v2alpha1/smt/proof.go b/store/v2alpha1/smt/proof.go deleted file mode 100644 index d247f1bf6617..000000000000 --- a/store/v2alpha1/smt/proof.go +++ /dev/null @@ -1,93 +0,0 @@ -package smt - -import ( - "bytes" - "crypto/sha256" - "encoding/gob" - "hash" - - "github.com/celestiaorg/smt" - "github.com/cosmos/cosmos-sdk/store/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/tendermint/tendermint/crypto/merkle" - tmmerkle "github.com/tendermint/tendermint/proto/tendermint/crypto" -) - -type HasherType byte - -const ( - SHA256 HasherType = iota -) - -const ( - ProofType = "smt" -) - -type ProofOp struct { - Root []byte - Key []byte - Hasher HasherType - Proof smt.SparseMerkleProof -} - -var _ merkle.ProofOperator = (*ProofOp)(nil) - -// NewProofOp returns a ProofOp for a SparseMerkleProof. -func NewProofOp(root, key []byte, hasher HasherType, proof smt.SparseMerkleProof) *ProofOp { - return &ProofOp{ - Root: root, - Key: key, - Hasher: hasher, - Proof: proof, - } -} - -func (p *ProofOp) Run(args [][]byte) ([][]byte, error) { - switch len(args) { - case 0: // non-membership proof - if !smt.VerifyProof(p.Proof, p.Root, p.Key, []byte{}, getHasher(p.Hasher)) { - return nil, sdkerrors.Wrapf(types.ErrInvalidProof, "proof did not verify absence of key: %s", p.Key) - } - case 1: // membership proof - if !smt.VerifyProof(p.Proof, p.Root, p.Key, args[0], getHasher(p.Hasher)) { - return nil, sdkerrors.Wrapf(types.ErrInvalidProof, "proof did not verify existence of key %s with given value %x", p.Key, args[0]) - } - default: - return nil, sdkerrors.Wrapf(types.ErrInvalidProof, "args must be length 0 or 1, got: %d", len(args)) - } - return [][]byte{p.Root}, nil -} - -func (p *ProofOp) GetKey() []byte { - return p.Key -} - -func (p *ProofOp) ProofOp() tmmerkle.ProofOp { - var data bytes.Buffer - enc := gob.NewEncoder(&data) - enc.Encode(p) - return tmmerkle.ProofOp{ - Type: "smt", - Key: p.Key, - Data: data.Bytes(), - } -} - -func ProofDecoder(pop tmmerkle.ProofOp) (merkle.ProofOperator, error) { - dec := gob.NewDecoder(bytes.NewBuffer(pop.Data)) - var proof ProofOp - err := dec.Decode(&proof) - if err != nil { - return nil, err - } - return &proof, nil -} - -func getHasher(hasher HasherType) hash.Hash { - switch hasher { - case SHA256: - return sha256.New() - default: - return nil - } -} diff --git a/store/v2alpha1/smt/proof_test.go b/store/v2alpha1/smt/proof_test.go deleted file mode 100644 index a5b3bf4869ec..000000000000 --- a/store/v2alpha1/smt/proof_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package smt_test - -import ( - "crypto/sha256" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/celestiaorg/smt" - "github.com/cosmos/cosmos-sdk/db/memdb" - smtstore "github.com/cosmos/cosmos-sdk/store/v2alpha1/smt" -) - -func TestProofOpInterface(t *testing.T) { - hasher := sha256.New() - nodes, values := memdb.NewDB(), memdb.NewDB() - tree := smt.NewSparseMerkleTree(nodes.ReadWriter(), values.ReadWriter(), hasher) - key := []byte("foo") - value := []byte("bar") - root, err := tree.Update(key, value) - require.NoError(t, err) - require.NotEmpty(t, root) - - proof, err := tree.Prove(key) - require.True(t, smt.VerifyProof(proof, root, key, value, hasher)) - - storeProofOp := smtstore.NewProofOp(root, key, smtstore.SHA256, proof) - require.NotNil(t, storeProofOp) - // inclusion proof - r, err := storeProofOp.Run([][]byte{value}) - assert.NoError(t, err) - assert.NotEmpty(t, r) - assert.Equal(t, root, r[0]) - - // inclusion proof - wrong value - should fail - r, err = storeProofOp.Run([][]byte{key}) - assert.Error(t, err) - assert.Empty(t, r) - - // exclusion proof - should fail - r, err = storeProofOp.Run([][]byte{}) - assert.Error(t, err) - assert.Empty(t, r) - - // invalid request - should fail - r, err = storeProofOp.Run([][]byte{key, key}) - assert.Error(t, err) - assert.Empty(t, r) - - // encode - tmProofOp := storeProofOp.ProofOp() - assert.NotNil(t, tmProofOp) - assert.Equal(t, smtstore.ProofType, tmProofOp.Type) - assert.Equal(t, key, tmProofOp.Key, key) - assert.NotEmpty(t, tmProofOp.Data) - - // decode - decoded, err := smtstore.ProofDecoder(tmProofOp) - assert.NoError(t, err) - assert.NotNil(t, decoded) - assert.Equal(t, key, decoded.GetKey()) - - // run proof after decoding - r, err = decoded.Run([][]byte{value}) - assert.NoError(t, err) - assert.NotEmpty(t, r) - assert.Equal(t, root, r[0]) -} diff --git a/store/v2alpha1/smt/store.go b/store/v2alpha1/smt/store.go deleted file mode 100644 index 3965eae18dbe..000000000000 --- a/store/v2alpha1/smt/store.go +++ /dev/null @@ -1,143 +0,0 @@ -package smt - -import ( - "crypto/sha256" - "errors" - - dbm "github.com/cosmos/cosmos-sdk/db" - "github.com/cosmos/cosmos-sdk/db/prefix" - "github.com/cosmos/cosmos-sdk/store/types" - - "github.com/celestiaorg/smt" - ics23 "github.com/confio/ics23/go" - tmcrypto "github.com/tendermint/tendermint/proto/tendermint/crypto" -) - -var ( - _ types.BasicKVStore = (*Store)(nil) - _ smt.MapStore = (dbMapStore{}) -) - -var ( - nodesPrefix = []byte{0} - valuesPrefix = []byte{1} - preimagesPrefix = []byte{2} - - errKeyEmpty = errors.New("key is empty or nil") - errValueNil = errors.New("value is nil") -) - -// Store Implements types.KVStore and CommitKVStore. -type Store struct { - tree *smt.SparseMerkleTree - values dbm.DBReadWriter - // Map hashed keys back to preimage - preimages dbm.DBReadWriter -} - -// An smt.MapStore that wraps Get to raise smt.InvalidKeyError; -// smt.SparseMerkleTree expects this error to be returned when a key is not found -type dbMapStore struct{ dbm.DBReadWriter } - -func NewStore(db dbm.DBReadWriter) *Store { - nodes := prefix.NewPrefixReadWriter(db, nodesPrefix) - values := prefix.NewPrefixReadWriter(db, valuesPrefix) - preimages := prefix.NewPrefixReadWriter(db, preimagesPrefix) - return &Store{ - tree: smt.NewSparseMerkleTree(dbMapStore{nodes}, dbMapStore{values}, sha256.New()), - values: values, - preimages: preimages, - } -} - -func LoadStore(db dbm.DBReadWriter, root []byte) *Store { - nodes := prefix.NewPrefixReadWriter(db, nodesPrefix) - values := prefix.NewPrefixReadWriter(db, valuesPrefix) - preimages := prefix.NewPrefixReadWriter(db, preimagesPrefix) - return &Store{ - tree: smt.ImportSparseMerkleTree(dbMapStore{nodes}, dbMapStore{values}, sha256.New(), root), - values: values, - preimages: preimages, - } -} - -func (s *Store) GetProof(key []byte) (*tmcrypto.ProofOps, error) { - if len(key) == 0 { - return nil, errKeyEmpty - } - proof, err := s.tree.Prove(key) - if err != nil { - return nil, err - } - op := NewProofOp(s.tree.Root(), key, SHA256, proof) - return &tmcrypto.ProofOps{Ops: []tmcrypto.ProofOp{op.ProofOp()}}, nil -} - -func (s *Store) GetProofICS23(key []byte) (*ics23.CommitmentProof, error) { - return createIcs23Proof(s, key) -} - -func (s *Store) Root() []byte { return s.tree.Root() } - -// BasicKVStore interface below: - -// Get returns nil iff key doesn't exist. Panics on nil or empty key. -func (s *Store) Get(key []byte) []byte { - if len(key) == 0 { - panic(errKeyEmpty) - } - val, err := s.tree.Get(key) - if err != nil { - panic(err) - } - return val -} - -// Has checks if a key exists. Panics on nil or empty key. -func (s *Store) Has(key []byte) bool { - if len(key) == 0 { - panic(errKeyEmpty) - } - has, err := s.tree.Has(key) - if err != nil { - panic(err) - } - return has -} - -// Set sets the key. Panics on nil key or value. -func (s *Store) Set(key []byte, value []byte) { - if len(key) == 0 { - panic(errKeyEmpty) - } - if value == nil { - panic(errValueNil) - } - _, err := s.tree.Update(key, value) - if err != nil { - panic(err) - } - path := sha256.Sum256(key) - s.preimages.Set(path[:], key) -} - -// Delete deletes the key. Panics on nil key. -func (s *Store) Delete(key []byte) { - if len(key) == 0 { - panic(errKeyEmpty) - } - _, _ = s.tree.Delete(key) - path := sha256.Sum256(key) - s.preimages.Delete(path[:]) -} - -func (ms dbMapStore) Get(key []byte) ([]byte, error) { - val, err := ms.DBReadWriter.Get(key) - if err != nil { - return nil, err - } - if val == nil { - return nil, &smt.InvalidKeyError{Key: key} - } - return val, nil -} diff --git a/store/v2alpha1/smt/store_test.go b/store/v2alpha1/smt/store_test.go deleted file mode 100644 index 2ad50be53dda..000000000000 --- a/store/v2alpha1/smt/store_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package smt_test - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/cosmos/cosmos-sdk/db/memdb" - store "github.com/cosmos/cosmos-sdk/store/v2alpha1/smt" -) - -func TestGetSetHasDelete(t *testing.T) { - db := memdb.NewDB() - s := store.NewStore(db.ReadWriter()) - - s.Set([]byte("foo"), []byte("bar")) - assert.Equal(t, []byte("bar"), s.Get([]byte("foo"))) - assert.Equal(t, true, s.Has([]byte("foo"))) - s.Delete([]byte("foo")) - assert.Equal(t, false, s.Has([]byte("foo"))) - - assert.Panics(t, func() { s.Get(nil) }, "Get(nil key) should panic") - assert.Panics(t, func() { s.Get([]byte{}) }, "Get(empty key) should panic") - assert.Panics(t, func() { s.Has(nil) }, "Has(nil key) should panic") - assert.Panics(t, func() { s.Has([]byte{}) }, "Has(empty key) should panic") - assert.Panics(t, func() { s.Set(nil, []byte("value")) }, "Set(nil key) should panic") - assert.Panics(t, func() { s.Set([]byte{}, []byte("value")) }, "Set(empty key) should panic") - assert.Panics(t, func() { s.Set([]byte("key"), nil) }, "Set(nil value) should panic") -} - -func TestLoadStore(t *testing.T) { - db := memdb.NewDB() - txn := db.ReadWriter() - s := store.NewStore(txn) - - s.Set([]byte{0}, []byte{0}) - s.Set([]byte{1}, []byte{1}) - s.Delete([]byte{1}) - root := s.Root() - - s = store.LoadStore(txn, root) - assert.Equal(t, []byte{0}, s.Get([]byte{0})) - assert.False(t, s.Has([]byte{1})) - s.Set([]byte{2}, []byte{2}) - assert.NotEqual(t, root, s.Root()) -} diff --git a/store/v2alpha1/transient/store.go b/store/v2alpha1/transient/store.go deleted file mode 100644 index 586cc4e9d36e..000000000000 --- a/store/v2alpha1/transient/store.go +++ /dev/null @@ -1,47 +0,0 @@ -package transient - -import ( - dbm "github.com/cosmos/cosmos-sdk/db" - "github.com/cosmos/cosmos-sdk/db/memdb" - pruningtypes "github.com/cosmos/cosmos-sdk/pruning/types" - "github.com/cosmos/cosmos-sdk/store/types" - "github.com/cosmos/cosmos-sdk/store/v2alpha1/dbadapter" -) - -var ( - _ types.KVStore = (*Store)(nil) - _ types.Committer = (*Store)(nil) -) - -// Store is a wrapper for a memory store which does not persist data. -type Store struct { - dbadapter.Store - conn dbm.DBConnection -} - -// NewStore constructs a new transient store. -func NewStore() *Store { - db := memdb.NewDB() - return &Store{ - Store: dbadapter.Store{DB: db.ReadWriter()}, - conn: db, - } -} - -// Implements Store. -func (ts *Store) GetStoreType() types.StoreType { - return types.StoreTypeTransient -} - -// Implements CommitStore -// Commit cleans up Store. -func (ts *Store) Commit() (id types.CommitID) { - ts.DB.Discard() - ts.Store = dbadapter.Store{DB: ts.conn.ReadWriter()} - return -} - -func (ts *Store) SetPruning(pruningtypes.PruningOptions) {} -func (ts *Store) GetPruning() pruningtypes.PruningOptions { return pruningtypes.PruningOptions{} } - -func (ts *Store) LastCommitID() (id types.CommitID) { return } diff --git a/store/v2alpha1/transient/store_test.go b/store/v2alpha1/transient/store_test.go deleted file mode 100644 index 272b7a34a8bc..000000000000 --- a/store/v2alpha1/transient/store_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package transient_test - -import ( - "bytes" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/cosmos/cosmos-sdk/store/types" - "github.com/cosmos/cosmos-sdk/store/v2alpha1/transient" -) - -var k, v = []byte("hello"), []byte("world") - -func TestTransientStore(t *testing.T) { - tstore := transient.NewStore() - require.Nil(t, tstore.Get(k)) - tstore.Set(k, v) - require.Equal(t, v, tstore.Get(k)) - tstore.Commit() - require.Nil(t, tstore.Get(k)) - - emptyCommitID := tstore.LastCommitID() - require.Equal(t, emptyCommitID.Version, int64(0)) - require.True(t, bytes.Equal(emptyCommitID.Hash, nil)) - require.Equal(t, types.StoreTypeTransient, tstore.GetStoreType()) -} diff --git a/store/v2alpha1/types.go b/store/v2alpha1/types.go deleted file mode 100644 index 44c97ff4f52d..000000000000 --- a/store/v2alpha1/types.go +++ /dev/null @@ -1,111 +0,0 @@ -package types - -import ( - "io" - - snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" - v1 "github.com/cosmos/cosmos-sdk/store/types" -) - -// Re-export relevant original store types -type ( - StoreKey = v1.StoreKey - StoreType = v1.StoreType - CommitID = v1.CommitID - StoreUpgrades = v1.StoreUpgrades - StoreRename = v1.StoreRename - Iterator = v1.Iterator - - TraceContext = v1.TraceContext - WriteListener = v1.WriteListener - - BasicKVStore = v1.BasicKVStore - KVStore = v1.KVStore - Committer = v1.Committer - CommitKVStore = v1.CommitKVStore - CacheKVStore = v1.CacheKVStore - Queryable = v1.Queryable - CacheWrap = v1.CacheWrap - - KVStoreKey = v1.KVStoreKey - MemoryStoreKey = v1.MemoryStoreKey - TransientStoreKey = v1.TransientStoreKey - - KVPair = v1.KVPair - StoreKVPair = v1.StoreKVPair -) - -// Re-export relevant constants, values and utility functions -const ( - StoreTypeMemory = v1.StoreTypeMemory - StoreTypeTransient = v1.StoreTypeTransient - StoreTypeDB = v1.StoreTypeDB - StoreTypeSMT = v1.StoreTypeSMT - StoreTypePersistent = v1.StoreTypePersistent -) - -var ( - NewKVStoreKey = v1.NewKVStoreKey - PrefixEndBytes = v1.PrefixEndBytes - KVStorePrefixIterator = v1.KVStorePrefixIterator - KVStoreReversePrefixIterator = v1.KVStoreReversePrefixIterator - - NewStoreKVPairWriteListener = v1.NewStoreKVPairWriteListener - - ProofOpSMTCommitment = v1.ProofOpSMTCommitment - ProofOpSimpleMerkleCommitment = v1.ProofOpSimpleMerkleCommitment - - CommitmentOpDecoder = v1.CommitmentOpDecoder - ProofOpFromMap = v1.ProofOpFromMap - NewSmtCommitmentOp = v1.NewSmtCommitmentOp -) - -// BasicMultiStore defines a minimal interface for accessing root state. -type BasicMultiStore interface { - // Returns a KVStore which has access only to the namespace of the StoreKey. - // Panics if the key is not found in the schema. - GetKVStore(StoreKey) KVStore -} - -// mixin interface for trace and listen methods -type rootStoreTraceListen interface { - TracingEnabled() bool - SetTracer(w io.Writer) - SetTraceContext(TraceContext) - ListeningEnabled(key StoreKey) bool - AddListeners(key StoreKey, listeners []WriteListener) -} - -// CommitMultiStore defines a complete interface for persistent root state, including -// (read-only) access to past versions, pruning, trace/listen, and state snapshots. -type CommitMultiStore interface { - BasicMultiStore - rootStoreTraceListen - Committer - snapshottypes.Snapshotter - - // Gets a read-only view of the store at a specific version. - // Returns an error if the version is not found. - GetVersion(int64) (BasicMultiStore, error) - // Closes the store and all backing transactions. - Close() error - // Returns a branched whose modifications are later merged back in. - CacheMultiStore() CacheMultiStore - // Defines the minimum version number that can be saved by this store. - SetInitialVersion(uint64) error -} - -// CacheMultiStore defines a branch of the root state which can be written back to the source store. -type CacheMultiStore interface { - BasicMultiStore - rootStoreTraceListen - - // Returns a branched whose modifications are later merged back in. - CacheMultiStore() CacheMultiStore - // Write all cached changes back to the source store. Note: this overwrites any intervening changes. - Write() -} - -// MultiStorePersistentCache provides inter-block (persistent) caching capabilities for a CommitMultiStore. -// TODO: placeholder. Implement and redefine this -type MultiStorePersistentCache = v1.MultiStorePersistentCache From f5aabe5e44dd8bc7d1c5b6f72f0d51e44c8916c6 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Fri, 24 Feb 2023 15:01:50 +0100 Subject: [PATCH 2/3] add changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4b6e9e207cd..2d752bb18aa2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,7 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## Improvements -* (store) [#]() Remove unmaintained and experimental `store/v2alpha1`. +* (store) [#15152](https://github.com/cosmos/cosmos-sdk/pull/15152) Remove unmaintained and experimental `store/v2alpha1`. * (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.10](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.46.10) - 2022-02-16 From 64e5f8d773a181c41e0beb4cec53c587b1947e01 Mon Sep 17 00:00:00 2001 From: Julien Robert Date: Fri, 24 Feb 2023 15:02:10 +0100 Subject: [PATCH 3/3] go mod tidy --- go.mod | 1 - go.sum | 2 -- 2 files changed, 3 deletions(-) diff --git a/go.mod b/go.mod index b0eb00e1a443..59dbcc12dc13 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,6 @@ require ( github.com/armon/go-metrics v0.4.1 github.com/bgentry/speakeasy v0.1.0 github.com/btcsuite/btcd/btcec/v2 v2.3.2 - github.com/celestiaorg/smt v0.3.0 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e github.com/cockroachdb/apd/v2 v2.0.2 github.com/coinbase/rosetta-sdk-go v0.7.9 diff --git a/go.sum b/go.sum index dd446840ea8c..18465df7d274 100644 --- a/go.sum +++ b/go.sum @@ -176,8 +176,6 @@ github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46f github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/celestiaorg/smt v0.3.0 h1:Hc6m8fIVRajrg/Saf8ivX4xw551LHzOs8kqeadd6h9s= -github.com/celestiaorg/smt v0.3.0/go.mod h1:/sdYDakowo/XaxS2Fl7CBqtuf/O2uTqF2zmAUFAtAiw= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=