diff --git a/store/basemulti/store.go b/store/basemulti/store.go new file mode 100644 index 000000000000..9ca0d59b2e32 --- /dev/null +++ b/store/basemulti/store.go @@ -0,0 +1,147 @@ +package basemulti + +import ( + "fmt" + + "github.com/tendermint/tendermint/crypto/merkle" + dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/cosmos/cosmos-sdk/store/types" +) + +type Store struct { + db dbm.DB + + kvstores map[types.KVStoreKey]types.CommitKVStore + kvkeysByName map[string]types.KVStoreKey + + pruning types.PruningStrategy +} + +func (store *Store) MountKVStoreWithDB(key types.KVStoreKey, db dbm.DB) { + if key == nil { + panic("MountStoreWithDB() key cannot be nil") + } + if _, ok := store.kvkeysByName[key.Name()]; ok { + panic(fmt.Sprintf("Store duplicate store key %v", key)) + } + + store.kvkeysByName[key.Name()] = key +} + +func (store *Store) GetCommitKVStore(key types.KVStoreKey) types.CommitKVStore { + return store.kvstores[key] +} + +func (store *Store) LoadMultiStoreVersion(ver int64) (err error) { + // Convert StoreInfos slice to map + var lastCommitID types.CommitID + infos := make(map[types.KVStoreKey]storeInfo) + if ver != 0 { + // Get commitInfo + cInfo, err := getCommitInfo(store.db, ver) + if err != nil { + return err + } + + for _, sInfo := range cInfo.storeInfos { + infos[store.nameToKVKey(sInfo.Name)] = sInfo + } + + lastCommitID = cInfo.CommitID() + } + + for _, key := range store.kvkeysByName { + var id types.CommitID + if info, ok := infos[key]; ok { + id = info.Core.CommitID + } + kvstore := key.NewStore() + db := dbm.NewPrefixDB(store.db, []byte("s/k:"+key.Name()+"/")) + err = kvstore.LoadKVStoreVersion(db, id) + if err != nil { + return + } + + kvstore.SetPruning(store.pruning) + } +} + +func (store *Store) nameToKVKey(name string) types.KVStoreKey { + for key := range kvstores { + if key.Name() == name { + return key + } + } +} + +// ------------------------------- +// storeInfo + +// storeInfo contains the name and core reference for an +// underlying store. It is the leaf of the Stores top +// level simple merkle tree + +type storeInfo struct { + Name string + Core storeCore +} + +type storeCore struct { + CommitID types.CommitID + // ... maybe add more state +} + +// ------------------------------ +// commitInfo + +// NOTE: keep commitInfo a simple immutable struct. +type commitInfo struct { + // Version + Version int64 + + // types.Store info for + storeInfos []storeInfo +} + +// Hash returns the simple merkle root hash of the stores sorted by name. +func (ci commitInfo) Hash() []byte { + // TODO cache to ci.hash []byte + m := make(map[string]merkle.Hasher, len(ci.StoreInfos)) + for _, storeInfo := range ci.StoreInfos { + m[storeInfo.Name] = storeInfo + } + return merkle.SimpleHashFromMap(m) +} + +func (ci commitInfo) CommitID() types.CommitID { + return types.CommitID{ + Version: ci.Version, + Hash: ci.Hash(), + } +} + +// ------------------------------- +// Misc. + +func getLatestVestoreion(db dbm.DB) (latest int64) { + latestBytes := db.Get([]byte("s/latest")) + if latestBytes == nil { + return 0 + } + cdc.MustUnmarshalBinary(latestBytes, &latest) + return +} + +func getCommitInfo(db dbm.DB, ver int64) (cInfo commitInfo, err error) { + cInfoBytes := db.Get([]byte(fmt.Sprintf("s/%d", ver))) + if cInfoBytes == nil { + err = fmt.Errorf("failed to get Store: no data") + } + + err = cdc.UnmarshalBinary(cInfoBytes, &cInfo) + if err != nil { + err = fmt.Errorf("failed to get Store: %v", err) + } + return +} diff --git a/store/basemulti/wire.go b/store/basemulti/wire.go new file mode 100644 index 000000000000..123ec0c86375 --- /dev/null +++ b/store/basemulti/wire.go @@ -0,0 +1,7 @@ +package basemulti + +import ( + "github.com/cosmos/cosmos-sdk/codec" +) + +var cdc = codec.New() diff --git a/store/cachemulti/store.go b/store/cachemulti/store.go index c43330167f14..41131c837aff 100644 --- a/store/cachemulti/store.go +++ b/store/cachemulti/store.go @@ -12,14 +12,14 @@ import ( //---------------------------------------- // Store -// Store holds many cache-wrapped stores. +// Store holds many cache-wrapped kvstores. // Implements MultiStore. -// TODO: support recursive multistores, +// TODO: support recursive multikvstores, // currently only using CacheKVStores type Store struct { db types.CacheKVStore - stores map[types.StoreKey]types.CacheKVStore - keysByName map[string]types.StoreKey + kvstores map[types.KVStoreKey]types.CacheKVStore + keysByName map[string]types.KVStoreKey tracer *types.Tracer tank *types.GasTank @@ -27,20 +27,20 @@ type Store struct { var _ types.CacheMultiStore = Store{} -func NewStore(db dbm.DB, keysByName map[string]types.StoreKey, stores map[types.StoreKey]types.CommitKVStore, tracer *types.Tracer, tank *types.GasTank) Store { +func NewStore(db dbm.DB, keysByName map[string]types.KVStoreKey, kvstores map[types.KVStoreKey]types.CommitKVStore, tracer *types.Tracer, tank *types.GasTank) Store { cms := Store{ db: cache.NewStore(dbadapter.NewStore(db)), - stores: make(map[types.StoreKey]types.CacheKVStore, len(stores)), + kvstores: make(map[types.KVStoreKey]types.CacheKVStore, len(kvstores)), keysByName: keysByName, tracer: tracer, tank: tank, } - for key, store := range stores { + for key, store := range kvstores { if tracer.Enabled() { - cms.stores[key] = cache.NewStore(trace.NewStore(store, tracer)) + cms.kvstores[key] = cache.NewStore(trace.NewStore(store, tracer)) } else { - cms.stores[key] = cache.NewStore(store) + cms.kvstores[key] = cache.NewStore(store) } } @@ -49,16 +49,16 @@ func NewStore(db dbm.DB, keysByName map[string]types.StoreKey, stores map[types. func newCacheMultiStoreFromCMS(cms Store) Store { cms2 := Store{ - db: cache.NewStore(cms.db), - stores: make(map[types.StoreKey]types.CacheKVStore, len(cms.stores)), - tracer: cms.tracer, + db: cache.NewStore(cms.db), + kvstores: make(map[types.KVStoreKey]types.CacheKVStore, len(cms.kvstores)), + tracer: cms.tracer, } - for key, store := range cms.stores { + for key, store := range cms.kvstores { if cms2.tracer.Enabled() { - cms2.stores[key] = cache.NewStore(trace.NewStore(store, cms.tracer)) + cms2.kvstores[key] = cache.NewStore(trace.NewStore(store, cms.tracer)) } else { - cms2.stores[key] = cache.NewStore(store) + cms2.kvstores[key] = cache.NewStore(store) } } @@ -78,7 +78,7 @@ func (cms Store) GetGasTank() *types.GasTank { // Implements CacheMultiStore. func (cms Store) Write() { cms.db.Write() - for _, store := range cms.stores { + for _, store := range cms.kvstores { store.Write() } } @@ -89,6 +89,6 @@ func (cms Store) CacheWrap() types.CacheMultiStore { } // Implements MultiStore. -func (cms Store) GetKVStore(key types.StoreKey) types.KVStore { - return cms.stores[key].(types.KVStore) +func (cms Store) GetKVStore(key types.KVStoreKey) types.KVStore { + return cms.kvstores[key].(types.KVStore) } diff --git a/store/iavl/key.go b/store/iavl/key.go index e70efef409c9..4a94f18f93e2 100644 --- a/store/iavl/key.go +++ b/store/iavl/key.go @@ -6,7 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/store/types" ) -var _ types.StoreKey = (*StoreKey)(nil) +var _ types.KVStoreKey = (*StoreKey)(nil) // StoreKey is used for accessing substores. // Only the pointer value should ever be used - it functions as a capabilities key. @@ -33,6 +33,6 @@ func (key *StoreKey) String() string { } // Implements StoreKey -func (key *StoreKey) NewStore() types.CommitStore { +func (key *StoreKey) NewStore() types.CommitKVStore { return &Store{} } diff --git a/store/iavlmulti/store.go b/store/iavlmulti/store.go new file mode 100644 index 000000000000..b1734350d3a1 --- /dev/null +++ b/store/iavlmulti/store.go @@ -0,0 +1,25 @@ +package iavlmulti + +import ( + dbm "github.com/tendermint/tendermint/libs/db" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/cosmos-sdk/store/cachemulti" + "github.com/cosmos/cosmos-sdk/store/iavl" + "github.com/cosmos/cosmos-sdk/store/types" +) + +// iavlmulti.Store works similar with rootmulti.Store +// but stores +type Store struct { + db dbm.DB + + kvstores map[types.KVStoreKey]iavl.KVStore +} + +var _ types.CommitMultiStore = (*Store)(nil) + +func (store *Store) CacheWrap() types.CacheMultiStore { + return cachemulti.NewStore(store.db, store.keysByName, store.iavlstores) +} diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index 51f75016255d..9e23d628e283 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -27,9 +27,9 @@ type Store struct { db dbm.DB lastCommitID types.CommitID pruning types.PruningStrategy - storesParams map[types.StoreKey]storeParams - kvstores map[types.StoreKey]types.CommitKVStore - keysByName map[string]types.StoreKey + storeParams map[types.KVStoreKey]storeParams + kvstores map[types.KVStoreKey]types.CommitKVStore + keysByName map[string]types.KVStoreKey tracer *types.Tracer tank *types.GasTank @@ -41,10 +41,10 @@ var _ types.Queryable = (*Store)(nil) // nolint func NewStore(db dbm.DB) *Store { return &Store{ - db: db, - storesParams: make(map[types.StoreKey]storeParams), - kvstores: make(map[types.StoreKey]types.CommitKVStore), - keysByName: make(map[string]types.StoreKey), + db: db, + storeParams: make(map[types.KVStoreKey]storeParams), + kvstores: make(map[types.KVStoreKey]types.CommitKVStore), + keysByName: make(map[string]types.KVStoreKey), tracer: new(types.Tracer), tank: new(types.GasTank), @@ -70,17 +70,17 @@ func (rs *Store) SetPruning(pruning types.PruningStrategy) { } // Implements CommitMultiStore. -func (rs *Store) MountStoreWithDB(key types.StoreKey, db dbm.DB) { +func (rs *Store) MountKVStoreWithDB(key types.KVStoreKey, db dbm.DB) { if key == nil { panic("MountStoreWithDB() key cannot be nil") } - if _, ok := rs.storesParams[key]; ok { + if _, ok := rs.storeParams[key]; ok { panic(fmt.Sprintf("Store duplicate store key %v", key)) } if _, ok := rs.keysByName[key.Name()]; ok { panic(fmt.Sprintf("Store duplicate store key name %v", key)) } - rs.storesParams[key] = storeParams{ + rs.storeParams[key] = storeParams{ key: key, db: db, } @@ -88,19 +88,14 @@ func (rs *Store) MountStoreWithDB(key types.StoreKey, db dbm.DB) { } // Implements CommitMultiStore. -func (rs *Store) GetCommitStore(key types.StoreKey) types.CommitStore { - return rs.kvstores[key] -} - -// Implements CommitMultiStore. -func (rs *Store) GetCommitKVStore(key types.StoreKey) types.CommitKVStore { +func (rs *Store) GetCommitKVStore(key types.KVStoreKey) types.CommitKVStore { return rs.kvstores[key] } /* // Recursive MultiStore not yet implemented // Implements CommitMultiStore -func (rs *Store) GetCommitMultiStore(key types.StoreKey) types.CommitMultiStore { +func (rs *Store) GetCommitMultiStore(key types.KVStoreKey) types.CommitMultiStore { return rs.multistores[key] } */ @@ -115,7 +110,7 @@ func (rs *Store) LoadLatestVersion() error { func (rs *Store) LoadMultiStoreVersion(ver int64) error { // Convert StoreInfos slice to map var lastCommitID types.CommitID - infos := make(map[types.StoreKey]storeInfo) + infos := make(map[types.KVStoreKey]storeInfo) if ver != 0 { // Get commitInfo cInfo, err := getCommitInfo(rs.db, ver) @@ -131,8 +126,8 @@ func (rs *Store) LoadMultiStoreVersion(ver int64) error { } // Load each Store - var newStores = make(map[types.StoreKey]types.CommitKVStore) - for key, storeParams := range rs.storesParams { + var newStores = make(map[types.KVStoreKey]types.CommitKVStore) + for key, storeParams := range rs.storeParams { var id types.CommitID if info, ok := infos[key]; ok { id = info.Core.CommitID @@ -146,7 +141,7 @@ func (rs *Store) LoadMultiStoreVersion(ver int64) error { // Success. rs.lastCommitID = lastCommitID - rs.stores = newStores + rs.kvstores = newStores return nil } @@ -163,7 +158,7 @@ func (rs *Store) Commit() types.CommitID { // Commit stores. version := rs.lastCommitID.Version + 1 - commitInfo := commitStores(version, rs.stores) + commitInfo := commitStores(version, rs.kvstores) // Need to update atomically. batch := rs.db.NewBatch() @@ -185,14 +180,14 @@ func (rs *Store) Commit() types.CommitID { // Implements types.MultiStore. func (rs *Store) CacheWrap() types.CacheMultiStore { - return cachemulti.NewStore(rs.db, rs.keysByName, rs.stores, rs.tracer, rs.tank) + return cachemulti.NewStore(rs.db, rs.keysByName, rs.kvstores, rs.tracer, rs.tank) } // GetKVStore implements the types.MultiStore interface. If tracing is enabled on the // Store, a wrapped TraceKVStore will be returned with the given // tracer, otherwise, the original types.KVStore will be returned. -func (rs *Store) GetKVStore(key types.StoreKey) types.KVStore { - store := rs.stores[key].(types.KVStore) +func (rs *Store) GetKVStore(key types.KVStoreKey) types.KVStore { + store := rs.kvstores[key].(types.KVStore) if rs.tracer.Enabled() { store = trace.NewStore(store, rs.tracer) @@ -206,14 +201,14 @@ func (rs *Store) GetKVStore(key types.StoreKey) types.KVStore { // getStoreByName will first convert the original name to // a special key, before looking up the types.CommitStore. // This is not exposed to the extensions (which will need the -// types.StoreKey), but is useful in main, and particularly app.Query, +// types.KVStoreKey), but is useful in main, and particularly app.Query, // in order to convert human strings into types.CommitStores. func (rs *Store) getStoreByName(name string) types.KVStore { key := rs.keysByName[name] if key == nil { return nil } - return rs.stores[key] + return rs.kvstores[key] } //---------------------- Query ------------------ @@ -277,7 +272,7 @@ func parsePath(path string) (storeName string, subpath string, err types.Error) //---------------------------------------- -func (rs *Store) loadCommitKVStoreFromParams(key types.StoreKey, id types.CommitID, params storeParams) (store types.CommitKVStore, err error) { +func (rs *Store) loadCommitKVStoreFromParams(key types.KVStoreKey, id types.CommitID, params storeParams) (store types.CommitKVStore, err error) { var db dbm.DB if params.db != nil { db = dbm.NewPrefixDB(params.db, []byte("s/_/")) @@ -293,35 +288,10 @@ func (rs *Store) loadCommitKVStoreFromParams(key types.StoreKey, id types.Commit store.SetPruning(rs.pruning) return - - // XXX: move to store subdirectories LoadKVStoreVersion - /* - switch params.typ { - case types.StoreTypeMulti: - panic("recursive types.MultiStores not yet supported") - // TODO: id? - // return NewCommitMultiStore(db, id) - case types.StoreTypeIAVL: - store, err = LoadIAVLStore(db, id, rs.pruning) - return - case types.StoreTypeDB: - panic("dbm.DB is not a types.CommitStore") - case types.StoreTypeTransient: - _, ok := key.(*types.TransientStoreKey) - if !ok { - err = fmt.Errorf("invalid types.StoreKey for types.StoreTypeTransient: %s", key.String()) - return - } - store = transient.NewStore() - return - default: - panic(fmt.Sprintf("unrecognized store type %v", params.typ)) - } - */ } -func (rs *Store) nameToKey(name string) types.StoreKey { - for key := range rs.storesParams { +func (rs *Store) nameToKey(name string) types.KVStoreKey { + for key := range rs.storeParams { if key.Name() == name { return key } @@ -333,7 +303,7 @@ func (rs *Store) nameToKey(name string) types.StoreKey { // storeParams type storeParams struct { - key types.StoreKey + key types.KVStoreKey db dbm.DB typ reflect.Type } @@ -422,7 +392,7 @@ func setLatestVersion(batch dbm.Batch, version int64) { } // Commits each store and returns a new commitInfo. -func commitStores(version int64, storeMap map[types.StoreKey]types.CommitKVStore) commitInfo { +func commitStores(version int64, storeMap map[types.KVStoreKey]types.CommitKVStore) commitInfo { storeInfos := make([]storeInfo, 0, len(storeMap)) for key, store := range storeMap { diff --git a/store/transient/key.go b/store/transient/key.go index 5be0549b223f..96a90455dc03 100644 --- a/store/transient/key.go +++ b/store/transient/key.go @@ -6,7 +6,7 @@ import ( "github.com/cosmos/cosmos-sdk/store/types" ) -var _ types.StoreKey = (*StoreKey)(nil) +var _ types.KVStoreKey = (*StoreKey)(nil) // StoreKey is used for indexing transient stores in a MultiStore type StoreKey struct { @@ -32,6 +32,6 @@ func (key *StoreKey) String() string { } // Implements StoreKey -func (key *StoreKey) NewStore() types.CommitStore { +func (key *StoreKey) NewStore() types.CommitKVStore { return &Store{} } diff --git a/store/types/store.go b/store/types/store.go index e0a10698cd51..9b351360536e 100644 --- a/store/types/store.go +++ b/store/types/store.go @@ -45,7 +45,7 @@ type Queryable interface { type MultiStore interface { //nolint // Convenience for fetching substores. - GetKVStore(StoreKey) KVStore + GetKVStore(KVStoreKey) KVStore // TODO: recursive multistore not yet supported // GetMultiStore(StoreKey) MultiStore @@ -67,19 +67,21 @@ type CommitMultiStore interface { CommitStore MultiStore - // Mount a store of type using the given db. + // Mount a KVStore of the key using the given db. // If db == nil, the new store will use the CommitMultiStore db. - MountStoreWithDB(key StoreKey, db dbm.DB) + MountKVStoreWithDB(key KVStoreKey, db dbm.DB) - // Panics on a nil key. - GetCommitStore(key StoreKey) CommitStore + // TODO: recursive multistore not implemented + // Mount a MultiStore of the key using the given db. + // If db == nil, the new store will use the CommitMultiStore db. + // MountMultiStoreWithDB(key MultiStoreKey, db dbm.DB) // Panics on a nil key. - GetCommitKVStore(key StoreKey) CommitKVStore + GetCommitKVStore(key KVStoreKey) CommitKVStore // Panics on a nil key. // TODO: recursive multistore not yet supported - // GetCommitMultiStore(key StoreKey) CommitMultiStore + // GetCommitMultiStore(key MultiStoreKey) CommitMultiStore // Load the latest persisted version. Called once after all // calls to Mount*Store() are complete. @@ -192,12 +194,22 @@ func (cid CommitID) String() string { //---------------------------------------- // Keys for accessing substores -// StoreKey is a key used to index stores in a MultiStore. -type StoreKey interface { +// KVStoreKey is a key used to index KVStores in a MultiStore. +type KVStoreKey interface { + Name() string + String() string + NewStore() CommitKVStore +} + +/* +// TODO: recursive multistore not implemented +// MultiStoreKey is a key used to index MultiStores in a MultiStore. +type MultiStoreKey interface { Name() string String() string - NewStore() CommitStore + NewStore() CommitMultiStore } +*/ // PrefixEndBytes returns the []byte that would end a // range query for all []byte with a certain prefix diff --git a/types/context.go b/types/context.go index 702cea7e3cda..bda2072bd6ee 100644 --- a/types/context.go +++ b/types/context.go @@ -74,7 +74,7 @@ func (c Context) Value(key interface{}) interface{} { } // KVStore fetches a KVStore from the MultiStore. -func (c Context) KVStore(key StoreKey) KVStore { +func (c Context) KVStore(key KVStoreKey) KVStore { return gas.NewStore( &GasTank{ GasMeter: c.GasMeter(), @@ -85,7 +85,7 @@ func (c Context) KVStore(key StoreKey) KVStore { } // TransientStore fetches a TransientStore from the MultiStore -func (c Context) TransientStore(key StoreKey) KVStore { +func (c Context) TransientStore(key KVStoreKey) KVStore { return gas.NewStore( &GasTank{ GasMeter: c.GasMeter(), diff --git a/types/store.go b/types/store.go index d9d40a70540e..d9f92bd8337e 100644 --- a/types/store.go +++ b/types/store.go @@ -22,8 +22,8 @@ type ( KVPair = types.KVPair - StoreKey = types.StoreKey - KVStoreKey = iavl.StoreKey + KVStoreKey = types.KVStoreKey + IAVLStoreKey = iavl.StoreKey TransientStoreKey = transient.StoreKey Gas = types.Gas @@ -57,7 +57,7 @@ func PrefixEndBytes(prefix []byte) []byte { func InclusiveEndBytes(inclusiveBytes []byte) []byte { return types.InclusiveEndBytes(inclusiveBytes) } -func NewKVStoreKey(name string) *KVStoreKey { +func NewKVStoreKey(name string) *IAVLStoreKey { return iavl.NewKey(name) } func NewTransientStoreKey(name string) *TransientStoreKey {