diff --git a/PENDING.md b/PENDING.md index d84b7ddb9f4f..9fdc6e1ce3c7 100644 --- a/PENDING.md +++ b/PENDING.md @@ -52,6 +52,7 @@ IMPROVEMENTS * [\#3420](https://github.com/cosmos/cosmos-sdk/issues/3420) Added maximum length to governance proposal descriptions and titles * SDK + * [\#2986](https://github.com/cosmos/cosmos-sdk/pull/2986) Store Refactor * \#3435 Test that store implementations do not allow nil values * Tendermint diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 950125180c85..ef5c692b9bb6 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -114,7 +114,7 @@ func (app *BaseApp) Name() string { // SetCommitMultiStoreTracer sets the store tracer on the BaseApp's underlying // CommitMultiStore. func (app *BaseApp) SetCommitMultiStoreTracer(w io.Writer) { - app.cms.WithTracer(w) + app.cms.SetTracer(w) } // Mount IAVL or DB stores to the provided keys in the BaseApp multistore @@ -483,8 +483,7 @@ func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res // BeginBlock implements the ABCI application interface. func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) { if app.cms.TracingEnabled() { - app.cms.ResetTraceContext() - app.cms.WithTracingContext(sdk.TraceContext( + app.cms.SetTracingContext(sdk.TraceContext( map[string]interface{}{"blockHeight": req.Header.Height}, )) } @@ -679,7 +678,7 @@ func (app *BaseApp) cacheTxContext(ctx sdk.Context, txBytes []byte) ( // TODO: https://github.com/cosmos/cosmos-sdk/issues/2824 msCache := ms.CacheMultiStore() if msCache.TracingEnabled() { - msCache = msCache.WithTracingContext( + msCache = msCache.SetTracingContext( sdk.TraceContext( map[string]interface{}{ "txHash": fmt.Sprintf("%X", tmhash.Sum(txBytes)), @@ -813,7 +812,7 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk // EndBlock implements the ABCI application interface. func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) { if app.deliverState.ms.TracingEnabled() { - app.deliverState.ms = app.deliverState.ms.ResetTraceContext().(sdk.CacheMultiStore) + app.deliverState.ms = app.deliverState.ms.SetTracingContext(nil).(sdk.CacheMultiStore) } if app.endBlocker != nil { diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index acccd1fbf6fe..82ee0bd9c3d7 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -7,7 +7,7 @@ import ( "os" "testing" - "github.com/cosmos/cosmos-sdk/store" + store "github.com/cosmos/cosmos-sdk/store/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/client/context/query.go b/client/context/query.go index cf70b3cea35f..11cd8720f49d 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -10,6 +10,7 @@ import ( "strings" + "github.com/cosmos/cosmos-sdk/store/rootmulti" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/merkle" cmn "github.com/tendermint/tendermint/libs/common" @@ -17,8 +18,6 @@ import ( tmliteProxy "github.com/tendermint/tendermint/lite/proxy" rpcclient "github.com/tendermint/tendermint/rpc/client" tmtypes "github.com/tendermint/tendermint/types" - - "github.com/cosmos/cosmos-sdk/store" ) // GetNode returns an RPC client. If the context's client is not defined, an @@ -207,7 +206,7 @@ func (ctx CLIContext) verifyProof(queryPath string, resp abci.ResponseQuery) err } // TODO: Instead of reconstructing, stash on CLIContext field? - prt := store.DefaultProofRuntime() + prt := rootmulti.DefaultProofRuntime() // TODO: Better convention for path? storeName, err := parseQueryStorePath(queryPath) @@ -247,7 +246,7 @@ func isQueryStoreWithProof(path string) bool { return false case paths[0] != "store": return false - case store.RequireProof("/" + paths[2]): + case rootmulti.RequireProof("/" + paths[2]): return true } diff --git a/cmd/gaia/cmd/gaiad/main.go b/cmd/gaia/cmd/gaiad/main.go index 0d6e8a92fd57..00ebb9667a55 100644 --- a/cmd/gaia/cmd/gaiad/main.go +++ b/cmd/gaia/cmd/gaiad/main.go @@ -60,7 +60,7 @@ func main() { func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application { return app.NewGaiaApp( logger, db, traceStore, true, - baseapp.SetPruning(store.NewPruningOptions(viper.GetString("pruning"))), + baseapp.SetPruning(store.NewPruningOptionsFromString(viper.GetString("pruning"))), baseapp.SetMinGasPrices(viper.GetString(server.FlagMinGasPrices)), ) } diff --git a/cmd/gaia/cmd/gaiadebug/hack.go b/cmd/gaia/cmd/gaiadebug/hack.go index aa70b3fb0ba5..eb4c8749bfca 100644 --- a/cmd/gaia/cmd/gaiadebug/hack.go +++ b/cmd/gaia/cmd/gaiadebug/hack.go @@ -50,7 +50,7 @@ func runHackCmd(cmd *cobra.Command, args []string) error { fmt.Println(err) os.Exit(1) } - app := NewGaiaApp(logger, db, baseapp.SetPruning(store.NewPruningOptions(viper.GetString("pruning")))) + app := NewGaiaApp(logger, db, baseapp.SetPruning(store.NewPruningOptionsFromString(viper.GetString("pruning")))) // print some info id := app.LastCommitID() diff --git a/server/mock/store.go b/server/mock/store.go index 3aecc1734407..2d18159aed01 100644 --- a/server/mock/store.go +++ b/server/mock/store.go @@ -26,19 +26,15 @@ func (ms multiStore) CacheWrapWithTrace(_ io.Writer, _ sdk.TraceContext) sdk.Cac panic("not implemented") } -func (ms multiStore) ResetTraceContext() sdk.MultiStore { - panic("not implemented") -} - func (ms multiStore) TracingEnabled() bool { panic("not implemented") } -func (ms multiStore) WithTracingContext(tc sdk.TraceContext) sdk.MultiStore { +func (ms multiStore) SetTracingContext(tc sdk.TraceContext) sdk.MultiStore { panic("not implemented") } -func (ms multiStore) WithTracer(w io.Writer) sdk.MultiStore { +func (ms multiStore) SetTracer(w io.Writer) sdk.MultiStore { panic("not implemented") } diff --git a/store/README.md b/store/README.md new file mode 100644 index 000000000000..54994374bcd6 --- /dev/null +++ b/store/README.md @@ -0,0 +1,130 @@ +# Store + +## CacheKV + +`cachekv.Store` is a wrapper `KVStore` which provides buffered writing / cached reading functionalities over the underlying `KVStore`. + +```go +type Store struct { + cache map[string]cValue + parent types.KVStore +} +``` + +### Get + +`Store.Get()` checks `Store.cache` first in order to find if there is any cached value associated with the key. If the value exists, the function returns it. If not, the function calls `Store.parent.Get()`, sets the key-value pair to the `Store.cache`, and returns it. + +### Set + +`Store.Set()` sets the key-value pair to the `Store.cache`. `cValue` has the field `dirty bool` which indicates whether the cached value is different from the underlying value. When `Store.Set()` cache new pair, the `cValue.dirty` is set true so when `Store.Write()` is called it can be written to the underlying store. + +### Iterator + +`Store.Iterator()` have to traverse on both caches items and the original items. In `Store.iterator()`, two iterators are generated for each of them, and merged. `memIterator` is essentially a slice of the `KVPair`s, used for cached items. `mergeIterator` is a combination of two iterators, where traverse happens ordered on both iterators. + +## CacheMulti + +`cachemulti.Store` is a wrapper `MultiStore` which provides buffered writing / cached reading functionalities over the underlying `MutliStore` + +```go +type Store struct { + db types.CacheKVStore + stores map[types.StoreKey] types.CacheWrap +} +``` + +`cachemulti.Store` cache wraps all substores in its constructor and hold them in `Store.stores`. `Store.GetKVStore()` returns the store from `Store.stores`, and `Store.Write()` recursively calls `CacheWrap.Write()` on the substores. + +## DBAdapter + +`dbadapter.Store` is a adapter for `dbm.DB` making it fulfilling the `KVStore` interface. + +```go +type Store struct { + dbm.DB +} +``` + +`dbadapter.Store` embeds `dbm.DB`, so most of the `KVStore` interface functions are implemented. The other functions(mostly miscellaneous) are manually implemented. + +## IAVL + +`iavl.Store` is a base-layer self-balancing merkle tree. It is guaranteed that + +1. Get & set operations are `O(log n)`, where `n` is the number of elements in the tree +2. Iteration efficiently returns the sorted elements within the range +3. Each tree version is immutable and can be retrieved even after a commit(depending on the pruning settings) + +Specification and implementation of IAVL tree can be found in [https://github.com/tendermint/iavl]. + +## GasKV + +`gaskv.Store` is a wrapper `KVStore` which provides gas consuming functionalities over the underlying `KVStore`. + +```go +type Store struct { + gasMeter types.GasMeter + gasConfig types.GasConfig + parent types.KVStore +} +``` + +When each `KVStore` methods are called, `gaskv.Store` automatically consumes appropriate amount of gas depending on the `Store.gasConfig`. + + +## Prefix + +`prefix.Store` is a wrapper `KVStore` which provides automatic key-prefixing functionalities over the underlying `KVStore`. + +```go +type Store struct { + parent types.KVStore + prefix []byte +} +``` + +When `Store.{Get, Set}()` is called, the store forwards the call to its parent, with the key prefixed with the `Store.prefix`. + +When `Store.Iterator()` is called, it does not simply prefix the `Store.prefix`, since it does not work as intended. In that case, some of the elements are traversed even they are not starting with the prefix. + +## RootMulti + +`rootmulti.Store` is a base-layer `MultiStore` where multiple `KVStore` can be mounted on it and retrieved via object-capability keys. The keys are memory addresses, so it is impossible to forge the key unless an object is a valid owner(or a receiver) of the key, according to the object capability principles. + +## TraceKV + +`tracekv.Store` is a wrapper `KVStore` which provides operation tracing functionalities over the underlying `KVStore`. + +```go +type Store struct { + parent types.KVStore + writer io.Writer + context types.TraceContext +} +``` + +When each `KVStore` methods are called, `tracekv.Store` automatically logs `traceOperation` to the `Store.writer`. + +```go +type traceOperation struct { + Operation operation + Key string + Value string + Metadata map[string]interface{} +} +``` + +`traceOperation.Metadata` is filled with `Store.context` when it is not nil. `TraceContext` is a `map[string]interface{}`. + +## Transient + +`transient.Store` is a base-layer `KVStore` which is automatically discarded at the end of the block. + +```go +type Store struct { + dbadapter.Store +} +``` + +`Store.Store` is a `dbadapter.Store` with a `dbm.NewMemDB()`. All `KVStore` methods are reused. When `Store.Commit()` is called, new `dbadapter.Store` is assigned, discarding previous reference and making it garbage collected. diff --git a/store/memiterator.go b/store/cachekv/memiterator.go similarity index 98% rename from store/memiterator.go rename to store/cachekv/memiterator.go index c9a026cb5ff1..972280c42a0a 100644 --- a/store/memiterator.go +++ b/store/cachekv/memiterator.go @@ -1,4 +1,4 @@ -package store +package cachekv import ( "bytes" diff --git a/store/cachemergeiterator.go b/store/cachekv/mergeiterator.go similarity index 95% rename from store/cachemergeiterator.go rename to store/cachekv/mergeiterator.go index cd739500de76..d45303e074e0 100644 --- a/store/cachemergeiterator.go +++ b/store/cachekv/mergeiterator.go @@ -1,7 +1,9 @@ -package store +package cachekv import ( "bytes" + + "github.com/cosmos/cosmos-sdk/store/types" ) // cacheMergeIterator merges a parent Iterator and a cache Iterator. @@ -12,14 +14,14 @@ import ( // // TODO: Optimize by memoizing. type cacheMergeIterator struct { - parent Iterator - cache Iterator + parent types.Iterator + cache types.Iterator ascending bool } -var _ Iterator = (*cacheMergeIterator)(nil) +var _ types.Iterator = (*cacheMergeIterator)(nil) -func newCacheMergeIterator(parent, cache Iterator, ascending bool) *cacheMergeIterator { +func newCacheMergeIterator(parent, cache types.Iterator, ascending bool) *cacheMergeIterator { iter := &cacheMergeIterator{ parent: parent, cache: cache, diff --git a/store/cachekv/store.go b/store/cachekv/store.go new file mode 100644 index 000000000000..673a101efa61 --- /dev/null +++ b/store/cachekv/store.go @@ -0,0 +1,196 @@ +package cachekv + +import ( + "bytes" + "io" + "sort" + "sync" + + cmn "github.com/tendermint/tendermint/libs/common" + dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/cosmos/cosmos-sdk/store/types" + + "github.com/cosmos/cosmos-sdk/store/tracekv" +) + +// If value is nil but deleted is false, it means the parent doesn't have the +// key. (No need to delete upon Write()) +type cValue struct { + value []byte + deleted bool + dirty bool +} + +// Store wraps an in-memory cache around an underlying types.KVStore. +type Store struct { + mtx sync.Mutex + cache map[string]cValue + parent types.KVStore +} + +var _ types.CacheKVStore = (*Store)(nil) + +// nolint +func NewStore(parent types.KVStore) *Store { + return &Store{ + cache: make(map[string]cValue), + parent: parent, + } +} + +// Implements Store. +func (store *Store) GetStoreType() types.StoreType { + return store.parent.GetStoreType() +} + +// Implements types.KVStore. +func (store *Store) Get(key []byte) (value []byte) { + store.mtx.Lock() + defer store.mtx.Unlock() + types.AssertValidKey(key) + + cacheValue, ok := store.cache[string(key)] + if !ok { + value = store.parent.Get(key) + store.setCacheValue(key, value, false, false) + } else { + value = cacheValue.value + } + + return value +} + +// Implements types.KVStore. +func (store *Store) Set(key []byte, value []byte) { + store.mtx.Lock() + defer store.mtx.Unlock() + types.AssertValidKey(key) + types.AssertValidValue(value) + + store.setCacheValue(key, value, false, true) +} + +// Implements types.KVStore. +func (store *Store) Has(key []byte) bool { + value := store.Get(key) + return value != nil +} + +// Implements types.KVStore. +func (store *Store) Delete(key []byte) { + store.mtx.Lock() + defer store.mtx.Unlock() + types.AssertValidKey(key) + + store.setCacheValue(key, nil, true, true) +} + +// Implements Cachetypes.KVStore. +func (store *Store) Write() { + store.mtx.Lock() + defer store.mtx.Unlock() + + // We need a copy of all of the keys. + // Not the best, but probably not a bottleneck depending. + keys := make([]string, 0, len(store.cache)) + for key, dbValue := range store.cache { + if dbValue.dirty { + keys = append(keys, key) + } + } + + sort.Strings(keys) + + // TODO: Consider allowing usage of Batch, which would allow the write to + // at least happen atomically. + for _, key := range keys { + cacheValue := store.cache[key] + if cacheValue.deleted { + store.parent.Delete([]byte(key)) + } else if cacheValue.value == nil { + // Skip, it already doesn't exist in parent. + } else { + store.parent.Set([]byte(key), cacheValue.value) + } + } + + // Clear the cache + store.cache = make(map[string]cValue) +} + +//---------------------------------------- +// To cache-wrap this Store further. + +// Implements CacheWrapper. +func (store *Store) CacheWrap() types.CacheWrap { + return NewStore(store) +} + +// CacheWrapWithTrace implements the CacheWrapper interface. +func (store *Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { + return NewStore(tracekv.NewStore(store, w, tc)) +} + +//---------------------------------------- +// Iteration + +// Implements types.KVStore. +func (store *Store) Iterator(start, end []byte) types.Iterator { + return store.iterator(start, end, true) +} + +// Implements types.KVStore. +func (store *Store) ReverseIterator(start, end []byte) types.Iterator { + return store.iterator(start, end, false) +} + +func (store *Store) iterator(start, end []byte, ascending bool) types.Iterator { + var parent, cache types.Iterator + + if ascending { + parent = store.parent.Iterator(start, end) + } else { + parent = store.parent.ReverseIterator(start, end) + } + + items := store.dirtyItems(start, end, ascending) + cache = newMemIterator(start, end, items) + + return newCacheMergeIterator(parent, cache, ascending) +} + +// Constructs a slice of dirty items, to use w/ memIterator. +func (store *Store) dirtyItems(start, end []byte, ascending bool) []cmn.KVPair { + items := make([]cmn.KVPair, 0) + + for key, cacheValue := range store.cache { + if !cacheValue.dirty { + continue + } + if dbm.IsKeyInDomain([]byte(key), start, end) { + items = append(items, cmn.KVPair{Key: []byte(key), Value: cacheValue.value}) + } + } + + sort.Slice(items, func(i, j int) bool { + if ascending { + return bytes.Compare(items[i].Key, items[j].Key) < 0 + } + return bytes.Compare(items[i].Key, items[j].Key) > 0 + }) + + return items +} + +//---------------------------------------- +// etc + +// Only entrypoint to mutate store.cache. +func (store *Store) setCacheValue(key, value []byte, deleted bool, dirty bool) { + store.cache[string(key)] = cValue{ + value: value, + deleted: deleted, + dirty: dirty, + } +} diff --git a/store/cachekvstore_test.go b/store/cachekv/store_test.go similarity index 91% rename from store/cachekvstore_test.go rename to store/cachekv/store_test.go index 8f43b0b3bb5e..30c8d148ac1d 100644 --- a/store/cachekvstore_test.go +++ b/store/cachekv/store_test.go @@ -1,4 +1,4 @@ -package store +package cachekv_test import ( "fmt" @@ -7,19 +7,23 @@ import ( "github.com/stretchr/testify/require" cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/cosmos/cosmos-sdk/store/cachekv" + "github.com/cosmos/cosmos-sdk/store/dbadapter" + "github.com/cosmos/cosmos-sdk/store/types" ) -func newCacheKVStore() CacheKVStore { - mem := dbStoreAdapter{dbm.NewMemDB()} - return NewCacheKVStore(mem) +func newCacheKVStore() types.CacheKVStore { + mem := dbadapter.Store{dbm.NewMemDB()} + return cachekv.NewStore(mem) } func keyFmt(i int) []byte { return bz(fmt.Sprintf("key%0.8d", i)) } func valFmt(i int) []byte { return bz(fmt.Sprintf("value%0.8d", i)) } func TestCacheKVStore(t *testing.T) { - mem := dbStoreAdapter{dbm.NewMemDB()} - st := NewCacheKVStore(mem) + mem := dbadapter.Store{dbm.NewMemDB()} + st := cachekv.NewStore(mem) require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") @@ -45,11 +49,11 @@ func TestCacheKVStore(t *testing.T) { require.Equal(t, valFmt(2), st.Get(keyFmt(1))) // make a new one, check it - st = NewCacheKVStore(mem) + st = cachekv.NewStore(mem) require.Equal(t, valFmt(2), st.Get(keyFmt(1))) // make a new one and delete - should not be removed from mem - st = NewCacheKVStore(mem) + st = cachekv.NewStore(mem) st.Delete(keyFmt(1)) require.Empty(t, st.Get(keyFmt(1))) require.Equal(t, mem.Get(keyFmt(1)), valFmt(2)) @@ -61,14 +65,14 @@ func TestCacheKVStore(t *testing.T) { } func TestCacheKVStoreNoNilSet(t *testing.T) { - mem := dbStoreAdapter{dbm.NewMemDB()} - st := NewCacheKVStore(mem) + mem := dbadapter.Store{dbm.NewMemDB()} + st := cachekv.NewStore(mem) require.Panics(t, func() { st.Set([]byte("key"), nil) }, "setting a nil value should panic") } func TestCacheKVStoreNested(t *testing.T) { - mem := dbStoreAdapter{dbm.NewMemDB()} - st := NewCacheKVStore(mem) + mem := dbadapter.Store{dbm.NewMemDB()} + st := cachekv.NewStore(mem) // set. check its there on st and not on mem. st.Set(keyFmt(1), valFmt(1)) @@ -76,7 +80,7 @@ func TestCacheKVStoreNested(t *testing.T) { require.Equal(t, valFmt(1), st.Get(keyFmt(1))) // make a new from st and check - st2 := NewCacheKVStore(st) + st2 := cachekv.NewStore(st) require.Equal(t, valFmt(1), st2.Get(keyFmt(1))) // update the value on st2, check it only effects st2 @@ -318,7 +322,7 @@ func randInt(n int) int { } // useful for replaying a error case if we find one -func doOp(st CacheKVStore, truth dbm.DB, op int, args ...int) { +func doOp(st types.CacheKVStore, truth dbm.DB, op int, args ...int) { switch op { case opSet: k := args[0] @@ -341,7 +345,7 @@ func doOp(st CacheKVStore, truth dbm.DB, op int, args ...int) { } } -func doRandomOp(st CacheKVStore, truth dbm.DB, maxKey int) { +func doRandomOp(st types.CacheKVStore, truth dbm.DB, maxKey int) { r := randInt(totalOps) switch r { case opSet: @@ -368,7 +372,7 @@ func doRandomOp(st CacheKVStore, truth dbm.DB, maxKey int) { //------------------------------------------------------------------------------------------- // iterate over whole domain -func assertIterateDomain(t *testing.T, st KVStore, expectedN int) { +func assertIterateDomain(t *testing.T, st types.KVStore, expectedN int) { itr := st.Iterator(nil, nil) var i = 0 for ; itr.Valid(); itr.Next() { @@ -380,7 +384,7 @@ func assertIterateDomain(t *testing.T, st KVStore, expectedN int) { require.Equal(t, expectedN, i) } -func assertIterateDomainCheck(t *testing.T, st KVStore, mem dbm.DB, r []keyRange) { +func assertIterateDomainCheck(t *testing.T, st types.KVStore, mem dbm.DB, r []keyRange) { // iterate over each and check they match the other itr := st.Iterator(nil, nil) itr2 := mem.Iterator(nil, nil) // ground truth @@ -410,7 +414,7 @@ func assertIterateDomainCheck(t *testing.T, st KVStore, mem dbm.DB, r []keyRange require.False(t, itr2.Valid()) } -func assertIterateDomainCompare(t *testing.T, st KVStore, mem dbm.DB) { +func assertIterateDomainCompare(t *testing.T, st types.KVStore, mem dbm.DB) { // iterate over each and check they match the other itr := st.Iterator(nil, nil) itr2 := mem.Iterator(nil, nil) // ground truth @@ -418,7 +422,7 @@ func assertIterateDomainCompare(t *testing.T, st KVStore, mem dbm.DB) { checkIterators(t, itr2, itr) } -func checkIterators(t *testing.T, itr, itr2 Iterator) { +func checkIterators(t *testing.T, itr, itr2 types.Iterator) { for ; itr.Valid(); itr.Next() { require.True(t, itr2.Valid()) k, v := itr.Key(), itr.Value() @@ -433,14 +437,14 @@ func checkIterators(t *testing.T, itr, itr2 Iterator) { //-------------------------------------------------------- -func setRange(st KVStore, mem dbm.DB, start, end int) { +func setRange(st types.KVStore, mem dbm.DB, start, end int) { for i := start; i < end; i++ { st.Set(keyFmt(i), valFmt(i)) mem.Set(keyFmt(i), valFmt(i)) } } -func deleteRange(st KVStore, mem dbm.DB, start, end int) { +func deleteRange(st types.KVStore, mem dbm.DB, start, end int) { for i := start; i < end; i++ { st.Delete(keyFmt(i)) mem.Delete(keyFmt(i)) diff --git a/store/cachekvstore.go b/store/cachekvstore.go deleted file mode 100644 index 28c344fd0b5b..000000000000 --- a/store/cachekvstore.go +++ /dev/null @@ -1,199 +0,0 @@ -package store - -import ( - "bytes" - "io" - "sort" - "sync" - - cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" -) - -// If value is nil but deleted is false, it means the parent doesn't have the -// key. (No need to delete upon Write()) -type cValue struct { - value []byte - deleted bool - dirty bool -} - -// cacheKVStore wraps an in-memory cache around an underlying KVStore. -type cacheKVStore struct { - mtx sync.Mutex - cache map[string]cValue - parent KVStore -} - -var _ CacheKVStore = (*cacheKVStore)(nil) - -// nolint -func NewCacheKVStore(parent KVStore) *cacheKVStore { - return &cacheKVStore{ - cache: make(map[string]cValue), - parent: parent, - } -} - -// Implements Store. -func (ci *cacheKVStore) GetStoreType() StoreType { - return ci.parent.GetStoreType() -} - -// Implements KVStore. -func (ci *cacheKVStore) Get(key []byte) (value []byte) { - ci.mtx.Lock() - defer ci.mtx.Unlock() - assertValidKey(key) - - cacheValue, ok := ci.cache[string(key)] - if !ok { - value = ci.parent.Get(key) - ci.setCacheValue(key, value, false, false) - } else { - value = cacheValue.value - } - - return value -} - -// Implements KVStore. -func (ci *cacheKVStore) Set(key []byte, value []byte) { - ci.mtx.Lock() - defer ci.mtx.Unlock() - assertValidKey(key) - assertValidValue(value) - - ci.setCacheValue(key, value, false, true) -} - -// Implements KVStore. -func (ci *cacheKVStore) Has(key []byte) bool { - value := ci.Get(key) - return value != nil -} - -// Implements KVStore. -func (ci *cacheKVStore) Delete(key []byte) { - ci.mtx.Lock() - defer ci.mtx.Unlock() - assertValidKey(key) - - ci.setCacheValue(key, nil, true, true) -} - -// Implements KVStore -func (ci *cacheKVStore) Prefix(prefix []byte) KVStore { - return prefixStore{ci, prefix} -} - -// Implements KVStore -func (ci *cacheKVStore) Gas(meter GasMeter, config GasConfig) KVStore { - return NewGasKVStore(meter, config, ci) -} - -// Implements CacheKVStore. -func (ci *cacheKVStore) Write() { - ci.mtx.Lock() - defer ci.mtx.Unlock() - - // We need a copy of all of the keys. - // Not the best, but probably not a bottleneck depending. - keys := make([]string, 0, len(ci.cache)) - for key, dbValue := range ci.cache { - if dbValue.dirty { - keys = append(keys, key) - } - } - - sort.Strings(keys) - - // TODO: Consider allowing usage of Batch, which would allow the write to - // at least happen atomically. - for _, key := range keys { - cacheValue := ci.cache[key] - if cacheValue.deleted { - ci.parent.Delete([]byte(key)) - } else if cacheValue.value == nil { - // Skip, it already doesn't exist in parent. - } else { - ci.parent.Set([]byte(key), cacheValue.value) - } - } - - // Clear the cache - ci.cache = make(map[string]cValue) -} - -//---------------------------------------- -// To cache-wrap this cacheKVStore further. - -// Implements CacheWrapper. -func (ci *cacheKVStore) CacheWrap() CacheWrap { - return NewCacheKVStore(ci) -} - -// CacheWrapWithTrace implements the CacheWrapper interface. -func (ci *cacheKVStore) CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap { - return NewCacheKVStore(NewTraceKVStore(ci, w, tc)) -} - -//---------------------------------------- -// Iteration - -// Implements KVStore. -func (ci *cacheKVStore) Iterator(start, end []byte) Iterator { - return ci.iterator(start, end, true) -} - -// Implements KVStore. -func (ci *cacheKVStore) ReverseIterator(start, end []byte) Iterator { - return ci.iterator(start, end, false) -} - -func (ci *cacheKVStore) iterator(start, end []byte, ascending bool) Iterator { - var parent, cache Iterator - - if ascending { - parent = ci.parent.Iterator(start, end) - } else { - parent = ci.parent.ReverseIterator(start, end) - } - - items := ci.dirtyItems(start, end, ascending) - cache = newMemIterator(start, end, items) - - return newCacheMergeIterator(parent, cache, ascending) -} - -// Constructs a slice of dirty items, to use w/ memIterator. -func (ci *cacheKVStore) dirtyItems(start, end []byte, ascending bool) []cmn.KVPair { - items := make([]cmn.KVPair, 0) - - for key, cacheValue := range ci.cache { - if !cacheValue.dirty { - continue - } - if dbm.IsKeyInDomain([]byte(key), start, end) { - items = append(items, cmn.KVPair{Key: []byte(key), Value: cacheValue.value}) - } - } - - sort.Slice(items, func(i, j int) bool { - if ascending { - return bytes.Compare(items[i].Key, items[j].Key) < 0 - } - return bytes.Compare(items[i].Key, items[j].Key) > 0 - }) - - return items -} - -// Only entrypoint to mutate ci.cache. -func (ci *cacheKVStore) setCacheValue(key, value []byte, deleted bool, dirty bool) { - ci.cache[string(key)] = cValue{ - value: value, - deleted: deleted, - dirty: dirty, - } -} diff --git a/store/cachemulti/store.go b/store/cachemulti/store.go new file mode 100644 index 000000000000..e1257a3385b2 --- /dev/null +++ b/store/cachemulti/store.go @@ -0,0 +1,135 @@ +package cachemulti + +import ( + "io" + + dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/cosmos/cosmos-sdk/store/cachekv" + "github.com/cosmos/cosmos-sdk/store/dbadapter" + "github.com/cosmos/cosmos-sdk/store/types" +) + +//---------------------------------------- +// Store + +// Store holds many cache-wrapped stores. +// Implements MultiStore. +// NOTE: a Store (and MultiStores in general) should never expose the +// keys for the substores. +type Store struct { + db types.CacheKVStore + stores map[types.StoreKey]types.CacheWrap + keys map[string]types.StoreKey + + traceWriter io.Writer + traceContext types.TraceContext +} + +var _ types.CacheMultiStore = Store{} + +func NewFromKVStore( + store types.KVStore, + stores map[types.StoreKey]types.CacheWrapper, keys map[string]types.StoreKey, + traceWriter io.Writer, traceContext types.TraceContext, +) Store { + cms := Store{ + db: cachekv.NewStore(store), + stores: make(map[types.StoreKey]types.CacheWrap, len(stores)), + keys: keys, + traceWriter: traceWriter, + traceContext: traceContext, + } + + for key, store := range stores { + if cms.TracingEnabled() { + cms.stores[key] = store.CacheWrapWithTrace(cms.traceWriter, cms.traceContext) + } else { + cms.stores[key] = store.CacheWrap() + } + } + + return cms +} + +func NewStore( + db dbm.DB, + stores map[types.StoreKey]types.CacheWrapper, keys map[string]types.StoreKey, + traceWriter io.Writer, traceContext types.TraceContext, +) Store { + return NewFromKVStore(dbadapter.Store{db}, stores, keys, traceWriter, traceContext) +} + +func newCacheMultiStoreFromCMS(cms Store) Store { + stores := make(map[types.StoreKey]types.CacheWrapper) + for k, v := range cms.stores { + stores[k] = v + } + return NewFromKVStore(cms.db, stores, nil, cms.traceWriter, cms.traceContext) +} + +// SetTracer sets the tracer for the MultiStore that the underlying +// stores will utilize to trace operations. A MultiStore is returned. +func (cms Store) SetTracer(w io.Writer) types.MultiStore { + cms.traceWriter = w + return cms +} + +// SetTracingContext updates the tracing context for the MultiStore by merging +// the given context with the existing context by key. Any existing keys will +// be overwritten. It is implied that the caller should update the context when +// necessary between tracing operations. It returns a modified MultiStore. +func (cms Store) SetTracingContext(tc types.TraceContext) types.MultiStore { + if cms.traceContext != nil { + for k, v := range tc { + cms.traceContext[k] = v + } + } else { + cms.traceContext = tc + } + + return cms +} + +// TracingEnabled returns if tracing is enabled for the MultiStore. +func (cms Store) TracingEnabled() bool { + return cms.traceWriter != nil +} + +// GetStoreType returns the type of the store. +func (cms Store) GetStoreType() types.StoreType { + return types.StoreTypeMulti +} + +// Write calls Write on each underlying store. +func (cms Store) Write() { + cms.db.Write() + for _, store := range cms.stores { + store.Write() + } +} + +// Implements CacheWrapper. +func (cms Store) CacheWrap() types.CacheWrap { + return cms.CacheMultiStore().(types.CacheWrap) +} + +// CacheWrapWithTrace implements the CacheWrapper interface. +func (cms Store) CacheWrapWithTrace(_ io.Writer, _ types.TraceContext) types.CacheWrap { + return cms.CacheWrap() +} + +// Implements MultiStore. +func (cms Store) CacheMultiStore() types.CacheMultiStore { + return newCacheMultiStoreFromCMS(cms) +} + +// GetStore returns an underlying Store by key. +func (cms Store) GetStore(key types.StoreKey) types.Store { + return cms.stores[key].(types.Store) +} + +// GetKVStore returns an underlying KVStore by key. +func (cms Store) GetKVStore(key types.StoreKey) types.KVStore { + return cms.stores[key].(types.KVStore) +} diff --git a/store/cachemultistore.go b/store/cachemultistore.go deleted file mode 100644 index ee19778559a0..000000000000 --- a/store/cachemultistore.go +++ /dev/null @@ -1,141 +0,0 @@ -package store - -import ( - "io" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -//---------------------------------------- -// cacheMultiStore - -// cacheMultiStore holds many cache-wrapped stores. -// Implements MultiStore. -// NOTE: a cacheMultiStore (and MultiStores in general) should never expose the -// keys for the substores. -type cacheMultiStore struct { - db CacheKVStore - stores map[StoreKey]CacheWrap - keysByName map[string]StoreKey - - traceWriter io.Writer - traceContext TraceContext -} - -var _ CacheMultiStore = cacheMultiStore{} - -func newCacheMultiStoreFromRMS(rms *rootMultiStore) cacheMultiStore { - cms := cacheMultiStore{ - db: NewCacheKVStore(dbStoreAdapter{rms.db}), - stores: make(map[StoreKey]CacheWrap, len(rms.stores)), - keysByName: rms.keysByName, - traceWriter: rms.traceWriter, - traceContext: rms.traceContext, - } - - for key, store := range rms.stores { - if cms.TracingEnabled() { - cms.stores[key] = store.CacheWrapWithTrace(cms.traceWriter, cms.traceContext) - } else { - cms.stores[key] = store.CacheWrap() - } - } - - return cms -} - -func newCacheMultiStoreFromCMS(cms cacheMultiStore) cacheMultiStore { - cms2 := cacheMultiStore{ - db: NewCacheKVStore(cms.db), - stores: make(map[StoreKey]CacheWrap, len(cms.stores)), - traceWriter: cms.traceWriter, - traceContext: cms.traceContext, - } - - for key, store := range cms.stores { - if cms2.TracingEnabled() { - cms2.stores[key] = store.CacheWrapWithTrace(cms2.traceWriter, cms2.traceContext) - } else { - cms2.stores[key] = store.CacheWrap() - } - } - - return cms2 -} - -// WithTracer sets the tracer for the MultiStore that the underlying -// stores will utilize to trace operations. A MultiStore is returned. -func (cms cacheMultiStore) WithTracer(w io.Writer) MultiStore { - cms.traceWriter = w - return cms -} - -// WithTracingContext updates the tracing context for the MultiStore by merging -// the given context with the existing context by key. Any existing keys will -// be overwritten. It is implied that the caller should update the context when -// necessary between tracing operations. It returns a modified MultiStore. -func (cms cacheMultiStore) WithTracingContext(tc TraceContext) MultiStore { - if cms.traceContext != nil { - for k, v := range tc { - cms.traceContext[k] = v - } - } else { - cms.traceContext = tc - } - - return cms -} - -// TracingEnabled returns if tracing is enabled for the MultiStore. -func (cms cacheMultiStore) TracingEnabled() bool { - return cms.traceWriter != nil -} - -// ResetTraceContext resets the current tracing context. -func (cms cacheMultiStore) ResetTraceContext() MultiStore { - cms.traceContext = nil - return cms -} - -// Implements Store. -func (cms cacheMultiStore) GetStoreType() StoreType { - return sdk.StoreTypeMulti -} - -// Implements CacheMultiStore. -func (cms cacheMultiStore) Write() { - cms.db.Write() - for _, store := range cms.stores { - store.Write() - } -} - -// Implements CacheWrapper. -func (cms cacheMultiStore) CacheWrap() CacheWrap { - return cms.CacheMultiStore().(CacheWrap) -} - -// CacheWrapWithTrace implements the CacheWrapper interface. -func (cms cacheMultiStore) CacheWrapWithTrace(_ io.Writer, _ TraceContext) CacheWrap { - return cms.CacheWrap() -} - -// Implements MultiStore. -func (cms cacheMultiStore) CacheMultiStore() CacheMultiStore { - return newCacheMultiStoreFromCMS(cms) -} - -// Implements MultiStore. -func (cms cacheMultiStore) GetStore(key StoreKey) Store { - return cms.stores[key].(Store) -} - -// Implements MultiStore. -func (cms cacheMultiStore) GetKVStore(key StoreKey) KVStore { - return cms.stores[key].(KVStore) -} - -// Implements MultiStore. -func (cms cacheMultiStore) GetKVStoreWithGas(meter sdk.GasMeter, config sdk.GasConfig, key StoreKey) KVStore { - return NewGasKVStore(meter, config, cms.GetKVStore(key)) -} diff --git a/store/dbadapter/store.go b/store/dbadapter/store.go new file mode 100644 index 000000000000..b96c6f5ccc08 --- /dev/null +++ b/store/dbadapter/store.go @@ -0,0 +1,34 @@ +package dbadapter + +import ( + "io" + + dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/cosmos/cosmos-sdk/store/cachekv" + "github.com/cosmos/cosmos-sdk/store/tracekv" + "github.com/cosmos/cosmos-sdk/store/types" +) + +// Wrapper type for dbm.Db with implementation of KVStore +type Store struct { + dbm.DB +} + +// GetStoreType returns the type of the store. +func (Store) GetStoreType() types.StoreType { + return types.StoreTypeDB +} + +// CacheWrap cache wraps 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)) +} + +// dbm.DB implements KVStore so we can CacheKVStore it. +var _ types.KVStore = Store{} diff --git a/store/dbstoreadapter.go b/store/dbstoreadapter.go deleted file mode 100644 index b662bcf45ddd..000000000000 --- a/store/dbstoreadapter.go +++ /dev/null @@ -1,67 +0,0 @@ -package store - -import ( - "io" - - dbm "github.com/tendermint/tendermint/libs/db" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// Wrapper type for dbm.Db with implementation of KVStore -type dbStoreAdapter struct { - dbm.DB -} - -// Implements Store. -func (dbStoreAdapter) GetStoreType() StoreType { - return sdk.StoreTypeDB -} - -// Implements KVStore. -func (dsa dbStoreAdapter) CacheWrap() CacheWrap { - return NewCacheKVStore(dsa) -} - -// CacheWrapWithTrace implements the KVStore interface. -func (dsa dbStoreAdapter) CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap { - return NewCacheKVStore(NewTraceKVStore(dsa, w, tc)) -} - -// Implements KVStore -func (dsa dbStoreAdapter) Prefix(prefix []byte) KVStore { - return prefixStore{dsa, prefix} -} - -// Implements KVStore -func (dsa dbStoreAdapter) Gas(meter GasMeter, config GasConfig) KVStore { - return NewGasKVStore(meter, config, dsa) -} - -// dbm.DB implements KVStore so we can CacheKVStore it. -var _ KVStore = dbStoreAdapter{} - -//---------------------------------------- -// commitDBStoreWrapper should only be used for simulation/debugging, -// as it doesn't compute any commit hash, and it cannot load older state. - -// Wrapper type for dbm.Db with implementation of KVStore -type commitDBStoreAdapter struct { - dbStoreAdapter -} - -func (cdsa commitDBStoreAdapter) Commit() CommitID { - return CommitID{ - Version: -1, - Hash: []byte("FAKE_HASH"), - } -} - -func (cdsa commitDBStoreAdapter) LastCommitID() CommitID { - return CommitID{ - Version: -1, - Hash: []byte("FAKE_HASH"), - } -} - -func (cdsa commitDBStoreAdapter) SetPruning(_ PruningOptions) {} diff --git a/store/firstlast.go b/store/firstlast.go index a47f1396d158..708a7d0d3dce 100644 --- a/store/firstlast.go +++ b/store/firstlast.go @@ -4,6 +4,8 @@ import ( "bytes" cmn "github.com/tendermint/tendermint/libs/common" + + "github.com/cosmos/cosmos-sdk/store/types" ) // Gets the first item. @@ -22,7 +24,7 @@ func Last(st KVStore, start, end []byte) (kv cmn.KVPair, ok bool) { iter := st.ReverseIterator(end, start) if !iter.Valid() { if v := st.Get(start); v != nil { - return cmn.KVPair{Key: cp(start), Value: cp(v)}, true + return cmn.KVPair{Key: types.Cp(start), Value: types.Cp(v)}, true } return kv, false } diff --git a/store/gaskvstore.go b/store/gaskv/store.go similarity index 55% rename from store/gaskvstore.go rename to store/gaskv/store.go index 790ae612fd54..e6bdbdc21ad5 100644 --- a/store/gaskvstore.go +++ b/store/gaskv/store.go @@ -1,25 +1,25 @@ -package store +package gaskv import ( "io" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/store/types" ) -var _ KVStore = &gasKVStore{} +var _ types.KVStore = &Store{} -// gasKVStore applies gas tracking to an underlying KVStore. It implements the +// Store applies gas tracking to an underlying KVStore. It implements the // KVStore interface. -type gasKVStore struct { - gasMeter sdk.GasMeter - gasConfig sdk.GasConfig - parent sdk.KVStore +type Store struct { + gasMeter types.GasMeter + gasConfig types.GasConfig + parent types.KVStore } -// NewGasKVStore returns a reference to a new GasKVStore. +// NewStore returns a reference to a new GasKVStore. // nolint -func NewGasKVStore(gasMeter sdk.GasMeter, gasConfig sdk.GasConfig, parent sdk.KVStore) *gasKVStore { - kvs := &gasKVStore{ +func NewStore(parent types.KVStore, gasMeter types.GasMeter, gasConfig types.GasConfig) *Store { + kvs := &Store{ gasMeter: gasMeter, gasConfig: gasConfig, parent: parent, @@ -28,62 +28,47 @@ func NewGasKVStore(gasMeter sdk.GasMeter, gasConfig sdk.GasConfig, parent sdk.KV } // Implements Store. -func (gs *gasKVStore) GetStoreType() sdk.StoreType { +func (gs *Store) GetStoreType() types.StoreType { return gs.parent.GetStoreType() } // Implements KVStore. -func (gs *gasKVStore) Get(key []byte) (value []byte) { - gs.gasMeter.ConsumeGas(gs.gasConfig.ReadCostFlat, sdk.GasReadCostFlatDesc) +func (gs *Store) Get(key []byte) (value []byte) { + gs.gasMeter.ConsumeGas(gs.gasConfig.ReadCostFlat, types.GasReadCostFlatDesc) value = gs.parent.Get(key) // TODO overflow-safe math? - gs.gasMeter.ConsumeGas(gs.gasConfig.ReadCostPerByte*sdk.Gas(len(value)), sdk.GasReadPerByteDesc) + gs.gasMeter.ConsumeGas(gs.gasConfig.ReadCostPerByte*types.Gas(len(value)), types.GasReadPerByteDesc) return value } // Implements KVStore. -func (gs *gasKVStore) Set(key []byte, value []byte) { - assertValidValue(value) - gs.gasMeter.ConsumeGas(gs.gasConfig.WriteCostFlat, sdk.GasWriteCostFlatDesc) +func (gs *Store) Set(key []byte, value []byte) { + types.AssertValidValue(value) + gs.gasMeter.ConsumeGas(gs.gasConfig.WriteCostFlat, types.GasWriteCostFlatDesc) // TODO overflow-safe math? - gs.gasMeter.ConsumeGas(gs.gasConfig.WriteCostPerByte*sdk.Gas(len(value)), sdk.GasWritePerByteDesc) + gs.gasMeter.ConsumeGas(gs.gasConfig.WriteCostPerByte*types.Gas(len(value)), types.GasWritePerByteDesc) gs.parent.Set(key, value) } // Implements KVStore. -func (gs *gasKVStore) Has(key []byte) bool { - gs.gasMeter.ConsumeGas(gs.gasConfig.HasCost, sdk.GasHasDesc) +func (gs *Store) Has(key []byte) bool { + gs.gasMeter.ConsumeGas(gs.gasConfig.HasCost, types.GasHasDesc) return gs.parent.Has(key) } // Implements KVStore. -func (gs *gasKVStore) Delete(key []byte) { +func (gs *Store) Delete(key []byte) { // charge gas to prevent certain attack vectors even though space is being freed - gs.gasMeter.ConsumeGas(gs.gasConfig.DeleteCost, sdk.GasDeleteDesc) + gs.gasMeter.ConsumeGas(gs.gasConfig.DeleteCost, types.GasDeleteDesc) gs.parent.Delete(key) } -// Implements KVStore -func (gs *gasKVStore) Prefix(prefix []byte) KVStore { - // Keep gasstore layer at the top - return &gasKVStore{ - gasMeter: gs.gasMeter, - gasConfig: gs.gasConfig, - parent: prefixStore{gs.parent, prefix}, - } -} - -// Implements KVStore -func (gs *gasKVStore) Gas(meter GasMeter, config GasConfig) KVStore { - return NewGasKVStore(meter, config, gs) -} - // Iterator implements the KVStore interface. It returns an iterator which // incurs a flat gas cost for seeking to the first key/value pair and a variable // gas cost based on the current value's length if the iterator is valid. -func (gs *gasKVStore) Iterator(start, end []byte) sdk.Iterator { +func (gs *Store) Iterator(start, end []byte) types.Iterator { return gs.iterator(start, end, true) } @@ -91,22 +76,22 @@ func (gs *gasKVStore) Iterator(start, end []byte) sdk.Iterator { // iterator which incurs a flat gas cost for seeking to the first key/value pair // and a variable gas cost based on the current value's length if the iterator // is valid. -func (gs *gasKVStore) ReverseIterator(start, end []byte) sdk.Iterator { +func (gs *Store) ReverseIterator(start, end []byte) types.Iterator { return gs.iterator(start, end, false) } // Implements KVStore. -func (gs *gasKVStore) CacheWrap() sdk.CacheWrap { +func (gs *Store) CacheWrap() types.CacheWrap { panic("cannot CacheWrap a GasKVStore") } // CacheWrapWithTrace implements the KVStore interface. -func (gs *gasKVStore) CacheWrapWithTrace(_ io.Writer, _ TraceContext) CacheWrap { +func (gs *Store) CacheWrapWithTrace(_ io.Writer, _ types.TraceContext) types.CacheWrap { panic("cannot CacheWrapWithTrace a GasKVStore") } -func (gs *gasKVStore) iterator(start, end []byte, ascending bool) sdk.Iterator { - var parent sdk.Iterator +func (gs *Store) iterator(start, end []byte, ascending bool) types.Iterator { + var parent types.Iterator if ascending { parent = gs.parent.Iterator(start, end) } else { @@ -122,12 +107,12 @@ func (gs *gasKVStore) iterator(start, end []byte, ascending bool) sdk.Iterator { } type gasIterator struct { - gasMeter sdk.GasMeter - gasConfig sdk.GasConfig - parent sdk.Iterator + gasMeter types.GasMeter + gasConfig types.GasConfig + parent types.Iterator } -func newGasIterator(gasMeter sdk.GasMeter, gasConfig sdk.GasConfig, parent sdk.Iterator) sdk.Iterator { +func newGasIterator(gasMeter types.GasMeter, gasConfig types.GasConfig, parent types.Iterator) types.Iterator { return &gasIterator{ gasMeter: gasMeter, gasConfig: gasConfig, @@ -180,6 +165,7 @@ func (gi *gasIterator) Close() { func (gi *gasIterator) consumeSeekGas() { value := gi.Value() - gi.gasMeter.ConsumeGas(gi.gasConfig.ReadCostPerByte*sdk.Gas(len(value)), sdk.GasValuePerByteDesc) - gi.gasMeter.ConsumeGas(gi.gasConfig.IterNextCostFlat, sdk.GasIterNextCostFlatDesc) + gi.gasMeter.ConsumeGas(gi.gasConfig.ReadCostPerByte*types.Gas(len(value)), types.GasValuePerByteDesc) + gi.gasMeter.ConsumeGas(gi.gasConfig.IterNextCostFlat, types.GasIterNextCostFlatDesc) + } diff --git a/store/gaskv/store_test.go b/store/gaskv/store_test.go new file mode 100644 index 000000000000..8d6acd86ef8a --- /dev/null +++ b/store/gaskv/store_test.go @@ -0,0 +1,78 @@ +package gaskv_test + +import ( + "fmt" + "testing" + + dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/cosmos/cosmos-sdk/store/dbadapter" + "github.com/cosmos/cosmos-sdk/store/gaskv" + stypes "github.com/cosmos/cosmos-sdk/store/types" + + "github.com/stretchr/testify/require" +) + +func newGasKVStore() stypes.KVStore { + meter := stypes.NewGasMeter(10000) + mem := dbadapter.Store{dbm.NewMemDB()} + return gaskv.NewStore(mem, meter, stypes.KVGasConfig()) +} + +func bz(s string) []byte { return []byte(s) } + +func keyFmt(i int) []byte { return bz(fmt.Sprintf("key%0.8d", i)) } +func valFmt(i int) []byte { return bz(fmt.Sprintf("value%0.8d", i)) } + +func TestGasKVStoreBasic(t *testing.T) { + mem := dbadapter.Store{dbm.NewMemDB()} + meter := stypes.NewGasMeter(10000) + st := gaskv.NewStore(mem, meter, stypes.KVGasConfig()) + require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") + st.Set(keyFmt(1), valFmt(1)) + require.Equal(t, valFmt(1), st.Get(keyFmt(1))) + st.Delete(keyFmt(1)) + require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") + require.Equal(t, meter.GasConsumed(), stypes.Gas(6429)) +} + +func TestGasKVStoreIterator(t *testing.T) { + mem := dbadapter.Store{dbm.NewMemDB()} + meter := stypes.NewGasMeter(10000) + st := gaskv.NewStore(mem, meter, stypes.KVGasConfig()) + require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") + require.Empty(t, st.Get(keyFmt(2)), "Expected `key2` to be empty") + st.Set(keyFmt(1), valFmt(1)) + st.Set(keyFmt(2), valFmt(2)) + iterator := st.Iterator(nil, nil) + ka := iterator.Key() + require.Equal(t, ka, keyFmt(1)) + va := iterator.Value() + require.Equal(t, va, valFmt(1)) + iterator.Next() + kb := iterator.Key() + require.Equal(t, kb, keyFmt(2)) + vb := iterator.Value() + require.Equal(t, vb, valFmt(2)) + iterator.Next() + require.False(t, iterator.Valid()) + require.Panics(t, iterator.Next) + require.Equal(t, meter.GasConsumed(), stypes.Gas(6987)) +} + +func TestGasKVStoreOutOfGasSet(t *testing.T) { + mem := dbadapter.Store{dbm.NewMemDB()} + meter := stypes.NewGasMeter(0) + st := gaskv.NewStore(mem, meter, stypes.KVGasConfig()) + require.Panics(t, func() { st.Set(keyFmt(1), valFmt(1)) }, "Expected out-of-gas") +} + +func TestGasKVStoreOutOfGasIterator(t *testing.T) { + mem := dbadapter.Store{dbm.NewMemDB()} + meter := stypes.NewGasMeter(20000) + st := gaskv.NewStore(mem, meter, stypes.KVGasConfig()) + st.Set(keyFmt(1), valFmt(1)) + iterator := st.Iterator(nil, nil) + iterator.Next() + require.Panics(t, func() { iterator.Value() }, "Expected out-of-gas") +} diff --git a/store/gaskvstore_test.go b/store/gaskvstore_test.go deleted file mode 100644 index c3a3e3928d1f..000000000000 --- a/store/gaskvstore_test.go +++ /dev/null @@ -1,111 +0,0 @@ -package store - -import ( - "testing" - - "github.com/tendermint/iavl" - dbm "github.com/tendermint/tendermint/libs/db" - - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/stretchr/testify/require" -) - -func newGasKVStore() KVStore { - meter := sdk.NewGasMeter(10000) - mem := dbStoreAdapter{dbm.NewMemDB()} - return NewGasKVStore(meter, sdk.KVGasConfig(), mem) -} - -func TestGasKVStoreBasic(t *testing.T) { - mem := dbStoreAdapter{dbm.NewMemDB()} - meter := sdk.NewGasMeter(10000) - st := NewGasKVStore(meter, sdk.KVGasConfig(), mem) - require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") - st.Set(keyFmt(1), valFmt(1)) - require.Equal(t, valFmt(1), st.Get(keyFmt(1))) - st.Delete(keyFmt(1)) - require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") - require.Equal(t, meter.GasConsumed(), sdk.Gas(6429)) -} - -func TestGasKVStoreNoNilSet(t *testing.T) { - st := newGasKVStore() - require.Panics(t, func() { st.Set([]byte("key"), nil) }, "setting a nil value should panic") -} - -func TestGasKVStoreIterator(t *testing.T) { - mem := dbStoreAdapter{dbm.NewMemDB()} - meter := sdk.NewGasMeter(10000) - st := NewGasKVStore(meter, sdk.KVGasConfig(), mem) - require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") - require.Empty(t, st.Get(keyFmt(2)), "Expected `key2` to be empty") - st.Set(keyFmt(1), valFmt(1)) - st.Set(keyFmt(2), valFmt(2)) - iterator := st.Iterator(nil, nil) - ka := iterator.Key() - require.Equal(t, ka, keyFmt(1)) - va := iterator.Value() - require.Equal(t, va, valFmt(1)) - iterator.Next() - kb := iterator.Key() - require.Equal(t, kb, keyFmt(2)) - vb := iterator.Value() - require.Equal(t, vb, valFmt(2)) - iterator.Next() - require.False(t, iterator.Valid()) - require.Panics(t, iterator.Next) - require.Equal(t, meter.GasConsumed(), sdk.Gas(6987)) -} - -func TestGasKVStoreOutOfGasSet(t *testing.T) { - mem := dbStoreAdapter{dbm.NewMemDB()} - meter := sdk.NewGasMeter(0) - st := NewGasKVStore(meter, sdk.KVGasConfig(), mem) - require.Panics(t, func() { st.Set(keyFmt(1), valFmt(1)) }, "Expected out-of-gas") -} - -func TestGasKVStoreOutOfGasIterator(t *testing.T) { - mem := dbStoreAdapter{dbm.NewMemDB()} - meter := sdk.NewGasMeter(20000) - st := NewGasKVStore(meter, sdk.KVGasConfig(), mem) - st.Set(keyFmt(1), valFmt(1)) - iterator := st.Iterator(nil, nil) - iterator.Next() - require.Panics(t, func() { iterator.Value() }, "Expected out-of-gas") -} - -func testGasKVStoreWrap(t *testing.T, store KVStore) { - meter := sdk.NewGasMeter(100000) - - store = store.Gas(meter, sdk.GasConfig{HasCost: 10}) - require.Equal(t, uint64(0), meter.GasConsumed()) - - store.Has([]byte("key")) - require.Equal(t, uint64(10), meter.GasConsumed()) - - store = store.Gas(meter, sdk.GasConfig{HasCost: 20}) - - store.Has([]byte("key")) - require.Equal(t, uint64(40), meter.GasConsumed()) -} - -func TestGasKVStoreWrap(t *testing.T) { - db := dbm.NewMemDB() - tree := iavl.NewMutableTree(db, cacheSize) - iavl := newIAVLStore(tree, numRecent, storeEvery) - testGasKVStoreWrap(t, iavl) - - st := NewCacheKVStore(iavl) - testGasKVStoreWrap(t, st) - - pref := st.Prefix([]byte("prefix")) - testGasKVStoreWrap(t, pref) - - dsa := dbStoreAdapter{dbm.NewMemDB()} - testGasKVStoreWrap(t, dsa) - - ts := newTransientStore() - testGasKVStoreWrap(t, ts) - -} diff --git a/store/iavlstore.go b/store/iavl/store.go similarity index 75% rename from store/iavlstore.go rename to store/iavl/store.go index 9bb6a8cb1413..29e375b55f65 100644 --- a/store/iavlstore.go +++ b/store/iavl/store.go @@ -1,4 +1,4 @@ -package store +package iavl import ( "fmt" @@ -11,7 +11,11 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" - sdk "github.com/cosmos/cosmos-sdk/types" + stypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/cosmos-sdk/store/cachekv" + "github.com/cosmos/cosmos-sdk/store/tracekv" ) const ( @@ -19,25 +23,25 @@ const ( ) // load the iavl store -func LoadIAVLStore(db dbm.DB, id CommitID, pruning sdk.PruningOptions) (CommitStore, error) { +func LoadStore(db dbm.DB, id types.CommitID, pruning types.PruningOptions) (types.CommitStore, error) { tree := iavl.NewMutableTree(db, defaultIAVLCacheSize) _, err := tree.LoadVersion(id.Version) if err != nil { return nil, err } - iavl := newIAVLStore(tree, int64(0), int64(0)) + iavl := UnsafeNewStore(tree, int64(0), int64(0)) iavl.SetPruning(pruning) return iavl, nil } //---------------------------------------- -var _ KVStore = (*iavlStore)(nil) -var _ CommitStore = (*iavlStore)(nil) -var _ Queryable = (*iavlStore)(nil) +var _ types.KVStore = (*Store)(nil) +var _ types.CommitStore = (*Store)(nil) +var _ types.Queryable = (*Store)(nil) -// iavlStore Implements KVStore and CommitStore. -type iavlStore struct { +// Store Implements types.KVStore and CommitStore. +type Store struct { // The underlying tree. tree *iavl.MutableTree @@ -56,8 +60,8 @@ type iavlStore struct { // CONTRACT: tree should be fully loaded. // nolint: unparam -func newIAVLStore(tree *iavl.MutableTree, numRecent int64, storeEvery int64) *iavlStore { - st := &iavlStore{ +func UnsafeNewStore(tree *iavl.MutableTree, numRecent int64, storeEvery int64) *Store { + st := &Store{ tree: tree, numRecent: numRecent, storeEvery: storeEvery, @@ -66,7 +70,7 @@ func newIAVLStore(tree *iavl.MutableTree, numRecent int64, storeEvery int64) *ia } // Implements Committer. -func (st *iavlStore) Commit() CommitID { +func (st *Store) Commit() types.CommitID { // Save a new version. hash, version, err := st.tree.SaveVersion() if err != nil { @@ -86,85 +90,75 @@ func (st *iavlStore) Commit() CommitID { } } - return CommitID{ + return types.CommitID{ Version: version, Hash: hash, } } // Implements Committer. -func (st *iavlStore) LastCommitID() CommitID { - return CommitID{ +func (st *Store) LastCommitID() types.CommitID { + return types.CommitID{ Version: st.tree.Version(), Hash: st.tree.Hash(), } } // Implements Committer. -func (st *iavlStore) SetPruning(opt sdk.PruningOptions) { +func (st *Store) SetPruning(opt types.PruningOptions) { st.numRecent = opt.KeepRecent() st.storeEvery = opt.KeepEvery() } // VersionExists returns whether or not a given version is stored. -func (st *iavlStore) VersionExists(version int64) bool { +func (st *Store) VersionExists(version int64) bool { return st.tree.VersionExists(version) } // Implements Store. -func (st *iavlStore) GetStoreType() StoreType { - return sdk.StoreTypeIAVL +func (st *Store) GetStoreType() types.StoreType { + return types.StoreTypeIAVL } // Implements Store. -func (st *iavlStore) CacheWrap() CacheWrap { - return NewCacheKVStore(st) +func (st *Store) CacheWrap() types.CacheWrap { + return cachekv.NewStore(st) } // CacheWrapWithTrace implements the Store interface. -func (st *iavlStore) CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap { - return NewCacheKVStore(NewTraceKVStore(st, w, tc)) +func (st *Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { + return cachekv.NewStore(tracekv.NewStore(st, w, tc)) } -// Implements KVStore. -func (st *iavlStore) Set(key, value []byte) { - assertValidValue(value) +// Implements types.KVStore. +func (st *Store) Set(key, value []byte) { + stypes.AssertValidValue(value) st.tree.Set(key, value) } -// Implements KVStore. -func (st *iavlStore) Get(key []byte) (value []byte) { +// Implements types.KVStore. +func (st *Store) Get(key []byte) (value []byte) { _, v := st.tree.Get(key) return v } -// Implements KVStore. -func (st *iavlStore) Has(key []byte) (exists bool) { +// Implements types.KVStore. +func (st *Store) Has(key []byte) (exists bool) { return st.tree.Has(key) } -// Implements KVStore. -func (st *iavlStore) Delete(key []byte) { +// Implements types.KVStore. +func (st *Store) Delete(key []byte) { st.tree.Remove(key) } -// Implements KVStore -func (st *iavlStore) Prefix(prefix []byte) KVStore { - return prefixStore{st, prefix} -} - -// Implements KVStore -func (st *iavlStore) Gas(meter GasMeter, config GasConfig) KVStore { - return NewGasKVStore(meter, config, st) -} - -// Implements KVStore. -func (st *iavlStore) Iterator(start, end []byte) Iterator { +// Implements types.KVStore. +func (st *Store) Iterator(start, end []byte) types.Iterator { return newIAVLIterator(st.tree.ImmutableTree, start, end, true) } -// Implements KVStore. -func (st *iavlStore) ReverseIterator(start, end []byte) Iterator { +// Implements types.KVStore. +func (st *Store) ReverseIterator(start, end []byte) types.Iterator { return newIAVLIterator(st.tree.ImmutableTree, start, end, false) } @@ -189,10 +183,10 @@ func getHeight(tree *iavl.MutableTree, req abci.RequestQuery) int64 { // 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 (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) { +func (st *Store) Query(req abci.RequestQuery) (res abci.ResponseQuery) { if len(req.Data) == 0 { msg := "Query cannot be zero length" - return sdk.ErrTxDecode(msg).QueryResult() + return types.ErrTxDecode(msg).QueryResult() } tree := st.tree @@ -237,14 +231,14 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) { } case "/subspace": - var KVs []KVPair + var KVs []types.KVPair subspace := req.Data res.Key = subspace - iterator := sdk.KVStorePrefixIterator(st, subspace) + iterator := stypes.KVStorePrefixIterator(st, subspace) for ; iterator.Valid(); iterator.Next() { - KVs = append(KVs, KVPair{Key: iterator.Key(), Value: iterator.Value()}) + KVs = append(KVs, types.KVPair{Key: iterator.Key(), Value: iterator.Value()}) } iterator.Close() @@ -252,7 +246,7 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) { default: msg := fmt.Sprintf("Unexpected Query path: %v", req.Path) - return sdk.ErrUnknownRequest(msg).QueryResult() + return types.ErrUnknownRequest(msg).QueryResult() } return @@ -260,7 +254,7 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) { //---------------------------------------- -// Implements Iterator. +// Implements types.Iterator. type iavlIterator struct { // Underlying store tree *iavl.ImmutableTree @@ -289,7 +283,7 @@ type iavlIterator struct { value []byte // The current value } -var _ Iterator = (*iavlIterator)(nil) +var _ types.Iterator = (*iavlIterator)(nil) // newIAVLIterator will create a new iavlIterator. // CONTRACT: Caller must release the iavlIterator, as each one creates a new @@ -297,8 +291,8 @@ var _ Iterator = (*iavlIterator)(nil) func newIAVLIterator(tree *iavl.ImmutableTree, start, end []byte, ascending bool) *iavlIterator { iter := &iavlIterator{ tree: tree, - start: cp(start), - end: cp(end), + start: stypes.Cp(start), + end: stypes.Cp(end), ascending: ascending, iterCh: make(chan cmn.KVPair, 0), // Set capacity > 0? quitCh: make(chan struct{}), @@ -331,12 +325,12 @@ func (iter *iavlIterator) initRoutine() { close(iter.initCh) } -// Implements Iterator. +// Implements types.Iterator. func (iter *iavlIterator) Domain() (start, end []byte) { return iter.start, iter.end } -// Implements Iterator. +// Implements types.Iterator. func (iter *iavlIterator) Valid() bool { iter.waitInit() iter.mtx.Lock() @@ -346,7 +340,7 @@ func (iter *iavlIterator) Valid() bool { return validity } -// Implements Iterator. +// Implements types.Iterator. func (iter *iavlIterator) Next() { iter.waitInit() iter.mtx.Lock() @@ -356,7 +350,7 @@ func (iter *iavlIterator) Next() { iter.mtx.Unlock() } -// Implements Iterator. +// Implements types.Iterator. func (iter *iavlIterator) Key() []byte { iter.waitInit() iter.mtx.Lock() @@ -367,7 +361,7 @@ func (iter *iavlIterator) Key() []byte { return key } -// Implements Iterator. +// Implements types.Iterator. func (iter *iavlIterator) Value() []byte { iter.waitInit() iter.mtx.Lock() @@ -378,7 +372,7 @@ func (iter *iavlIterator) Value() []byte { return val } -// Implements Iterator. +// Implements types.Iterator. func (iter *iavlIterator) Close() { close(iter.quitCh) } @@ -422,14 +416,3 @@ func (iter *iavlIterator) assertIsValid(unlockMutex bool) { panic("invalid iterator") } } - -//---------------------------------------- - -func cp(bz []byte) (ret []byte) { - if bz == nil { - return nil - } - ret = make([]byte, len(bz)) - copy(ret, bz) - return ret -} diff --git a/store/iavlstore_test.go b/store/iavl/store_test.go similarity index 88% rename from store/iavlstore_test.go rename to store/iavl/store_test.go index 78140ba327f0..360fa712cd93 100644 --- a/store/iavlstore_test.go +++ b/store/iavl/store_test.go @@ -1,4 +1,4 @@ -package store +package iavl import ( "fmt" @@ -11,7 +11,7 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types" ) var ( @@ -29,7 +29,7 @@ var ( ) // make a tree with data from above and save it -func newAlohaTree(t *testing.T, db dbm.DB) (*iavl.MutableTree, CommitID) { +func newAlohaTree(t *testing.T, db dbm.DB) (*iavl.MutableTree, types.CommitID) { tree := iavl.NewMutableTree(db, cacheSize) for k, v := range treeData { tree.Set([]byte(k), []byte(v)) @@ -41,13 +41,13 @@ func newAlohaTree(t *testing.T, db dbm.DB) (*iavl.MutableTree, CommitID) { } hash, ver, err := tree.SaveVersion() require.Nil(t, err) - return tree, CommitID{ver, hash} + return tree, types.CommitID{ver, hash} } func TestIAVLStoreGetSetHasDelete(t *testing.T) { db := dbm.NewMemDB() tree, _ := newAlohaTree(t, db) - iavlStore := newIAVLStore(tree, numRecent, storeEvery) + iavlStore := UnsafeNewStore(tree, numRecent, storeEvery) key := "hello" @@ -72,14 +72,14 @@ func TestIAVLStoreGetSetHasDelete(t *testing.T) { func TestIAVLStoreNoNilSet(t *testing.T) { db := dbm.NewMemDB() tree, _ := newAlohaTree(t, db) - iavlStore := newIAVLStore(tree, numRecent, storeEvery) + iavlStore := UnsafeNewStore(tree, numRecent, storeEvery) require.Panics(t, func() { iavlStore.Set([]byte("key"), nil) }, "setting a nil value should panic") } func TestIAVLIterator(t *testing.T) { db := dbm.NewMemDB() tree, _ := newAlohaTree(t, db) - iavlStore := newIAVLStore(tree, numRecent, storeEvery) + iavlStore := UnsafeNewStore(tree, numRecent, storeEvery) iter := iavlStore.Iterator([]byte("aloha"), []byte("hellz")) expected := []string{"aloha", "hello"} var i int @@ -152,7 +152,7 @@ func TestIAVLIterator(t *testing.T) { func TestIAVLReverseIterator(t *testing.T) { db := dbm.NewMemDB() tree := iavl.NewMutableTree(db, cacheSize) - iavlStore := newIAVLStore(tree, numRecent, storeEvery) + iavlStore := UnsafeNewStore(tree, numRecent, storeEvery) iavlStore.Set([]byte{0x00}, []byte("0")) iavlStore.Set([]byte{0x00, 0x00}, []byte("0 0")) @@ -183,7 +183,7 @@ func TestIAVLReverseIterator(t *testing.T) { func TestIAVLPrefixIterator(t *testing.T) { db := dbm.NewMemDB() tree := iavl.NewMutableTree(db, cacheSize) - iavlStore := newIAVLStore(tree, numRecent, storeEvery) + iavlStore := UnsafeNewStore(tree, numRecent, storeEvery) iavlStore.Set([]byte("test1"), []byte("test1")) iavlStore.Set([]byte("test2"), []byte("test2")) @@ -197,7 +197,7 @@ func TestIAVLPrefixIterator(t *testing.T) { var i int - iter := sdk.KVStorePrefixIterator(iavlStore, []byte("test")) + iter := types.KVStorePrefixIterator(iavlStore, []byte("test")) expected := []string{"test1", "test2", "test3"} for i = 0; iter.Valid(); iter.Next() { expectedKey := expected[i] @@ -209,7 +209,7 @@ func TestIAVLPrefixIterator(t *testing.T) { iter.Close() require.Equal(t, len(expected), i) - iter = sdk.KVStorePrefixIterator(iavlStore, []byte{byte(55), byte(255), byte(255)}) + iter = types.KVStorePrefixIterator(iavlStore, []byte{byte(55), byte(255), byte(255)}) expected2 := [][]byte{ {byte(55), byte(255), byte(255), byte(0)}, {byte(55), byte(255), byte(255), byte(1)}, @@ -225,7 +225,7 @@ func TestIAVLPrefixIterator(t *testing.T) { iter.Close() require.Equal(t, len(expected), i) - iter = sdk.KVStorePrefixIterator(iavlStore, []byte{byte(255), byte(255)}) + iter = types.KVStorePrefixIterator(iavlStore, []byte{byte(255), byte(255)}) expected2 = [][]byte{ {byte(255), byte(255), byte(0)}, {byte(255), byte(255), byte(1)}, @@ -245,7 +245,7 @@ func TestIAVLPrefixIterator(t *testing.T) { func TestIAVLReversePrefixIterator(t *testing.T) { db := dbm.NewMemDB() tree := iavl.NewMutableTree(db, cacheSize) - iavlStore := newIAVLStore(tree, numRecent, storeEvery) + iavlStore := UnsafeNewStore(tree, numRecent, storeEvery) iavlStore.Set([]byte("test1"), []byte("test1")) iavlStore.Set([]byte("test2"), []byte("test2")) @@ -259,7 +259,7 @@ func TestIAVLReversePrefixIterator(t *testing.T) { var i int - iter := sdk.KVStoreReversePrefixIterator(iavlStore, []byte("test")) + iter := types.KVStoreReversePrefixIterator(iavlStore, []byte("test")) expected := []string{"test3", "test2", "test1"} for i = 0; iter.Valid(); iter.Next() { expectedKey := expected[i] @@ -270,7 +270,7 @@ func TestIAVLReversePrefixIterator(t *testing.T) { } require.Equal(t, len(expected), i) - iter = sdk.KVStoreReversePrefixIterator(iavlStore, []byte{byte(55), byte(255), byte(255)}) + iter = types.KVStoreReversePrefixIterator(iavlStore, []byte{byte(55), byte(255), byte(255)}) expected2 := [][]byte{ {byte(55), byte(255), byte(255), byte(255)}, {byte(55), byte(255), byte(255), byte(1)}, @@ -285,7 +285,7 @@ func TestIAVLReversePrefixIterator(t *testing.T) { } require.Equal(t, len(expected), i) - iter = sdk.KVStoreReversePrefixIterator(iavlStore, []byte{byte(255), byte(255)}) + iter = types.KVStoreReversePrefixIterator(iavlStore, []byte{byte(255), byte(255)}) expected2 = [][]byte{ {byte(255), byte(255), byte(255)}, {byte(255), byte(255), byte(1)}, @@ -301,7 +301,7 @@ func TestIAVLReversePrefixIterator(t *testing.T) { require.Equal(t, len(expected), i) } -func nextVersion(iavl *iavlStore) { +func nextVersion(iavl *Store) { key := []byte(fmt.Sprintf("Key for tree: %d", iavl.LastCommitID().Version)) value := []byte(fmt.Sprintf("Value for tree: %d", iavl.LastCommitID().Version)) iavl.Set(key, value) @@ -364,7 +364,7 @@ type pruneState struct { func testPruning(t *testing.T, numRecent int64, storeEvery int64, states []pruneState) { db := dbm.NewMemDB() tree := iavl.NewMutableTree(db, cacheSize) - iavlStore := newIAVLStore(tree, numRecent, storeEvery) + iavlStore := UnsafeNewStore(tree, numRecent, storeEvery) for step, state := range states { for _, ver := range state.stored { require.True(t, iavlStore.VersionExists(ver), @@ -383,7 +383,7 @@ func testPruning(t *testing.T, numRecent int64, storeEvery int64, states []prune func TestIAVLNoPrune(t *testing.T) { db := dbm.NewMemDB() tree := iavl.NewMutableTree(db, cacheSize) - iavlStore := newIAVLStore(tree, numRecent, int64(1)) + iavlStore := UnsafeNewStore(tree, numRecent, int64(1)) nextVersion(iavlStore) for i := 1; i < 100; i++ { for j := 1; j <= i; j++ { @@ -398,7 +398,7 @@ func TestIAVLNoPrune(t *testing.T) { func TestIAVLPruneEverything(t *testing.T) { db := dbm.NewMemDB() tree := iavl.NewMutableTree(db, cacheSize) - iavlStore := newIAVLStore(tree, int64(0), int64(0)) + iavlStore := UnsafeNewStore(tree, int64(0), int64(0)) nextVersion(iavlStore) for i := 1; i < 100; i++ { for j := 1; j < i; j++ { @@ -416,19 +416,19 @@ func TestIAVLPruneEverything(t *testing.T) { func TestIAVLStoreQuery(t *testing.T) { db := dbm.NewMemDB() tree := iavl.NewMutableTree(db, cacheSize) - iavlStore := newIAVLStore(tree, numRecent, storeEvery) + iavlStore := UnsafeNewStore(tree, numRecent, storeEvery) k1, v1 := []byte("key1"), []byte("val1") k2, v2 := []byte("key2"), []byte("val2") v3 := []byte("val3") ksub := []byte("key") - KVs0 := []KVPair{} - KVs1 := []KVPair{ + KVs0 := []types.KVPair{} + KVs1 := []types.KVPair{ {Key: k1, Value: v1}, {Key: k2, Value: v2}, } - KVs2 := []KVPair{ + KVs2 := []types.KVPair{ {Key: k1, Value: v3}, {Key: k2, Value: v2}, } @@ -443,7 +443,7 @@ func TestIAVLStoreQuery(t *testing.T) { // query subspace before anything set qres := iavlStore.Query(querySub) - require.Equal(t, uint32(sdk.CodeOK), qres.Code) + require.Equal(t, uint32(types.CodeOK), qres.Code) require.Equal(t, valExpSubEmpty, qres.Value) // set data @@ -452,24 +452,24 @@ func TestIAVLStoreQuery(t *testing.T) { // set data without commit, doesn't show up qres = iavlStore.Query(query) - require.Equal(t, uint32(sdk.CodeOK), qres.Code) + require.Equal(t, uint32(types.CodeOK), qres.Code) require.Nil(t, qres.Value) // commit it, but still don't see on old version cid = iavlStore.Commit() qres = iavlStore.Query(query) - require.Equal(t, uint32(sdk.CodeOK), qres.Code) + require.Equal(t, uint32(types.CodeOK), qres.Code) require.Nil(t, qres.Value) // but yes on the new version query.Height = cid.Version qres = iavlStore.Query(query) - require.Equal(t, uint32(sdk.CodeOK), qres.Code) + require.Equal(t, uint32(types.CodeOK), qres.Code) require.Equal(t, v1, qres.Value) // and for the subspace qres = iavlStore.Query(querySub) - require.Equal(t, uint32(sdk.CodeOK), qres.Code) + require.Equal(t, uint32(types.CodeOK), qres.Code) require.Equal(t, valExpSub1, qres.Value) // modify @@ -478,28 +478,28 @@ func TestIAVLStoreQuery(t *testing.T) { // query will return old values, as height is fixed qres = iavlStore.Query(query) - require.Equal(t, uint32(sdk.CodeOK), qres.Code) + require.Equal(t, uint32(types.CodeOK), qres.Code) require.Equal(t, v1, qres.Value) // update to latest in the query and we are happy query.Height = cid.Version qres = iavlStore.Query(query) - require.Equal(t, uint32(sdk.CodeOK), qres.Code) + require.Equal(t, uint32(types.CodeOK), qres.Code) require.Equal(t, v3, qres.Value) query2 := abci.RequestQuery{Path: "/key", Data: k2, Height: cid.Version} qres = iavlStore.Query(query2) - require.Equal(t, uint32(sdk.CodeOK), qres.Code) + require.Equal(t, uint32(types.CodeOK), qres.Code) require.Equal(t, v2, qres.Value) // and for the subspace qres = iavlStore.Query(querySub) - require.Equal(t, uint32(sdk.CodeOK), qres.Code) + require.Equal(t, uint32(types.CodeOK), qres.Code) require.Equal(t, valExpSub2, qres.Value) // default (height 0) will show latest -1 query0 := abci.RequestQuery{Path: "/key", Data: k1} qres = iavlStore.Query(query0) - require.Equal(t, uint32(sdk.CodeOK), qres.Code) + require.Equal(t, uint32(types.CodeOK), qres.Code) require.Equal(t, v1, qres.Value) } @@ -512,8 +512,8 @@ func BenchmarkIAVLIteratorNext(b *testing.B) { value := cmn.RandBytes(50) tree.Set(key, value) } - iavlStore := newIAVLStore(tree, numRecent, storeEvery) - iterators := make([]Iterator, b.N/treeSize) + iavlStore := UnsafeNewStore(tree, numRecent, storeEvery) + iterators := make([]types.Iterator, b.N/treeSize) for i := 0; i < len(iterators); i++ { iterators[i] = iavlStore.Iterator([]byte{0}, []byte{255, 255, 255, 255, 255}) } diff --git a/store/iavl/wire.go b/store/iavl/wire.go new file mode 100644 index 000000000000..43173f3e7895 --- /dev/null +++ b/store/iavl/wire.go @@ -0,0 +1,7 @@ +package iavl + +import ( + "github.com/cosmos/cosmos-sdk/codec" +) + +var cdc = codec.New() diff --git a/store/list.go b/store/list/list.go similarity index 93% rename from store/list.go rename to store/list/list.go index 542a77bf9b54..934dfaa0f1ce 100644 --- a/store/list.go +++ b/store/list/list.go @@ -1,11 +1,12 @@ -package store +package list import ( "fmt" "strconv" "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/cosmos-sdk/store/types" ) // Key for the length of the list @@ -22,11 +23,11 @@ func ElemKey(index uint64) []byte { // It panics when the element type cannot be (un/)marshalled by the codec type List struct { cdc *codec.Codec - store sdk.KVStore + store types.KVStore } // NewList constructs new List -func NewList(cdc *codec.Codec, store sdk.KVStore) List { +func NewList(cdc *codec.Codec, store types.KVStore) List { return List{ cdc: cdc, store: store, @@ -83,7 +84,7 @@ func (m List) Push(value interface{}) { // CONTRACT: No writes may happen within a domain while iterating over it. func (m List) Iterate(ptr interface{}, fn func(uint64) bool) { - iter := sdk.KVStorePrefixIterator(m.store, []byte{0x01}) + iter := types.KVStorePrefixIterator(m.store, []byte{0x01}) defer iter.Close() for ; iter.Valid(); iter.Next() { v := iter.Value() diff --git a/store/list_test.go b/store/list/list_test.go similarity index 61% rename from store/list_test.go rename to store/list/list_test.go index 6767457cd163..970c836bc3eb 100644 --- a/store/list_test.go +++ b/store/list/list_test.go @@ -1,34 +1,54 @@ -package store +package list import ( "math/rand" "testing" - "github.com/stretchr/testify/require" + dbm "github.com/tendermint/tendermint/libs/db" + "github.com/tendermint/tendermint/libs/log" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/rootmulti" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" ) +type TestStruct struct { + I uint64 + B bool +} + +func defaultComponents(key sdk.StoreKey) (sdk.Context, *codec.Codec) { + db := dbm.NewMemDB() + cms := rootmulti.NewStore(db) + cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) + cms.LoadLatestVersion() + ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger()) + cdc := codec.New() + return ctx, cdc +} func TestList(t *testing.T) { key := sdk.NewKVStoreKey("test") ctx, cdc := defaultComponents(key) store := ctx.KVStore(key) lm := NewList(cdc, store) - val := S{1, true} - var res S + val := TestStruct{1, true} + var res TestStruct lm.Push(val) require.Equal(t, uint64(1), lm.Len()) lm.Get(uint64(0), &res) require.Equal(t, val, res) - val = S{2, false} + val = TestStruct{2, false} lm.Set(uint64(0), val) lm.Get(uint64(0), &res) require.Equal(t, val, res) - val = S{100, false} + val = TestStruct{100, false} lm.Push(val) require.Equal(t, uint64(2), lm.Len()) lm.Get(uint64(1), &res) @@ -38,7 +58,7 @@ func TestList(t *testing.T) { require.Equal(t, uint64(2), lm.Len()) lm.Iterate(&res, func(index uint64) (brk bool) { - var temp S + var temp TestStruct lm.Get(index, &temp) require.Equal(t, temp, res) @@ -47,12 +67,12 @@ func TestList(t *testing.T) { }) lm.Iterate(&res, func(index uint64) (brk bool) { - lm.Set(index, S{res.I + 1, !res.B}) + lm.Set(index, TestStruct{res.I + 1, !res.B}) return }) lm.Get(uint64(0), &res) - require.Equal(t, S{3, true}, res) + require.Equal(t, TestStruct{3, true}, res) } func TestListRandom(t *testing.T) { diff --git a/store/prefixstore.go b/store/prefix/store.go similarity index 71% rename from store/prefixstore.go rename to store/prefix/store.go index 66a08f0eb707..a04995273baf 100644 --- a/store/prefixstore.go +++ b/store/prefix/store.go @@ -1,23 +1,31 @@ -package store +package prefix import ( "bytes" "io" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/store/cachekv" + "github.com/cosmos/cosmos-sdk/store/tracekv" + "github.com/cosmos/cosmos-sdk/store/types" ) -var _ KVStore = prefixStore{} +var _ types.KVStore = Store{} -// prefixStore is similar with tendermint/tendermint/libs/db/prefix_db +// Store is similar with tendermint/tendermint/libs/db/prefix_db // both gives access only to the limited subset of the store // for convinience or safety - -type prefixStore struct { - parent KVStore +type Store struct { + parent types.KVStore prefix []byte } +func NewStore(parent types.KVStore, prefix []byte) Store { + return Store{ + parent: parent, + prefix: prefix, + } +} + func cloneAppend(bz []byte, tail []byte) (res []byte) { res = make([]byte, len(bz)+len(tail)) copy(res, bz) @@ -25,64 +33,55 @@ func cloneAppend(bz []byte, tail []byte) (res []byte) { return } -func (s prefixStore) key(key []byte) (res []byte) { +func (s Store) key(key []byte) (res []byte) { if key == nil { - panic("nil key on prefixStore") + panic("nil key on Store") } res = cloneAppend(s.prefix, key) return } // Implements Store -func (s prefixStore) GetStoreType() StoreType { +func (s Store) GetStoreType() types.StoreType { return s.parent.GetStoreType() } // Implements CacheWrap -func (s prefixStore) CacheWrap() CacheWrap { - return NewCacheKVStore(s) +func (s Store) CacheWrap() types.CacheWrap { + return cachekv.NewStore(s) } // CacheWrapWithTrace implements the KVStore interface. -func (s prefixStore) CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap { - return NewCacheKVStore(NewTraceKVStore(s, w, tc)) +func (s Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { + return cachekv.NewStore(tracekv.NewStore(s, w, tc)) } // Implements KVStore -func (s prefixStore) Get(key []byte) []byte { +func (s Store) Get(key []byte) []byte { res := s.parent.Get(s.key(key)) return res } // Implements KVStore -func (s prefixStore) Has(key []byte) bool { +func (s Store) Has(key []byte) bool { return s.parent.Has(s.key(key)) } // Implements KVStore -func (s prefixStore) Set(key, value []byte) { - assertValidValue(value) +func (s Store) Set(key, value []byte) { + types.AssertValidKey(key) + types.AssertValidValue(value) s.parent.Set(s.key(key), value) } // Implements KVStore -func (s prefixStore) Delete(key []byte) { +func (s Store) Delete(key []byte) { s.parent.Delete(s.key(key)) } -// Implements KVStore -func (s prefixStore) Prefix(prefix []byte) KVStore { - return prefixStore{s, prefix} -} - -// Implements KVStore -func (s prefixStore) Gas(meter GasMeter, config GasConfig) KVStore { - return NewGasKVStore(meter, config, s) -} - // Implements KVStore // Check https://github.com/tendermint/tendermint/blob/master/libs/db/prefix_db.go#L106 -func (s prefixStore) Iterator(start, end []byte) Iterator { +func (s Store) Iterator(start, end []byte) types.Iterator { newstart := cloneAppend(s.prefix, start) var newend []byte @@ -99,7 +98,7 @@ func (s prefixStore) Iterator(start, end []byte) Iterator { // Implements KVStore // Check https://github.com/tendermint/tendermint/blob/master/libs/db/prefix_db.go#L129 -func (s prefixStore) ReverseIterator(start, end []byte) Iterator { +func (s Store) ReverseIterator(start, end []byte) types.Iterator { newstart := cloneAppend(s.prefix, start) var newend []byte @@ -114,16 +113,16 @@ func (s prefixStore) ReverseIterator(start, end []byte) Iterator { return newPrefixIterator(s.prefix, start, end, iter) } -var _ sdk.Iterator = (*prefixIterator)(nil) +var _ types.Iterator = (*prefixIterator)(nil) type prefixIterator struct { prefix []byte start, end []byte - iter Iterator + iter types.Iterator valid bool } -func newPrefixIterator(prefix, start, end []byte, parent Iterator) *prefixIterator { +func newPrefixIterator(prefix, start, end []byte, parent types.Iterator) *prefixIterator { return &prefixIterator{ prefix: prefix, start: start, @@ -185,9 +184,9 @@ func stripPrefix(key []byte, prefix []byte) []byte { return key[len(prefix):] } -// wrapping sdk.PrefixEndBytes +// wrapping types.PrefixEndBytes func cpIncr(bz []byte) []byte { - return sdk.PrefixEndBytes(bz) + return types.PrefixEndBytes(bz) } // copied from github.com/tendermint/tendermint/libs/db/util.go @@ -210,7 +209,7 @@ func cpDecr(bz []byte) (ret []byte) { return nil } -func skipOne(iter Iterator, skipKey []byte) { +func skipOne(iter types.Iterator, skipKey []byte) { if iter.Valid() { if bytes.Equal(iter.Key(), skipKey) { iter.Next() diff --git a/store/prefixstore_test.go b/store/prefix/store_test.go similarity index 79% rename from store/prefixstore_test.go rename to store/prefix/store_test.go index 4a4253af461e..04019ead30b0 100644 --- a/store/prefixstore_test.go +++ b/store/prefix/store_test.go @@ -1,4 +1,4 @@ -package store +package prefix import ( "math/rand" @@ -6,12 +6,24 @@ import ( "github.com/stretchr/testify/require" - "github.com/tendermint/iavl" + tiavl "github.com/tendermint/iavl" dbm "github.com/tendermint/tendermint/libs/db" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/store/dbadapter" + "github.com/cosmos/cosmos-sdk/store/gaskv" + "github.com/cosmos/cosmos-sdk/store/iavl" + "github.com/cosmos/cosmos-sdk/store/types" ) +// copied from iavl/store_test.go +var ( + cacheSize = 100 + numRecent int64 = 5 + storeEvery int64 = 3 +) + +func bz(s string) []byte { return []byte(s) } + type kvpair struct { key []byte value []byte @@ -30,7 +42,7 @@ func genRandomKVPairs(t *testing.T) []kvpair { return kvps } -func setRandomKVPairs(t *testing.T, store KVStore) []kvpair { +func setRandomKVPairs(t *testing.T, store types.KVStore) []kvpair { kvps := genRandomKVPairs(t) for _, kvp := range kvps { store.Set(kvp.key, kvp.value) @@ -38,9 +50,9 @@ func setRandomKVPairs(t *testing.T, store KVStore) []kvpair { return kvps } -func testPrefixStore(t *testing.T, baseStore KVStore, prefix []byte) { - prefixStore := baseStore.Prefix(prefix) - prefixPrefixStore := prefixStore.Prefix([]byte("prefix")) +func testPrefixStore(t *testing.T, baseStore types.KVStore, prefix []byte) { + prefixStore := NewStore(baseStore, prefix) + prefixPrefixStore := NewStore(prefixStore, []byte("prefix")) require.Panics(t, func() { prefixStore.Get(nil) }) require.Panics(t, func() { prefixStore.Set(nil, []byte{}) }) @@ -75,43 +87,29 @@ func testPrefixStore(t *testing.T, baseStore KVStore, prefix []byte) { func TestIAVLStorePrefix(t *testing.T) { db := dbm.NewMemDB() - tree := iavl.NewMutableTree(db, cacheSize) - iavlStore := newIAVLStore(tree, numRecent, storeEvery) + tree := tiavl.NewMutableTree(db, cacheSize) + iavlStore := iavl.UnsafeNewStore(tree, numRecent, storeEvery) testPrefixStore(t, iavlStore, []byte("test")) } -func TestCacheKVStorePrefix(t *testing.T) { - cacheStore := newCacheKVStore() - - testPrefixStore(t, cacheStore, []byte("test")) -} - -func TestGasKVStorePrefix(t *testing.T) { - meter := sdk.NewGasMeter(100000000) - mem := dbStoreAdapter{dbm.NewMemDB()} - gasStore := NewGasKVStore(meter, sdk.KVGasConfig(), mem) - - testPrefixStore(t, gasStore, []byte("test")) -} - func TestPrefixKVStoreNoNilSet(t *testing.T) { - meter := sdk.NewGasMeter(100000000) - mem := dbStoreAdapter{dbm.NewMemDB()} - gasStore := NewGasKVStore(meter, sdk.KVGasConfig(), mem) + meter := types.NewGasMeter(100000000) + mem := dbadapter.Store{dbm.NewMemDB()} + gasStore := gaskv.NewStore(mem, meter, types.KVGasConfig()) require.Panics(t, func() { gasStore.Set([]byte("key"), nil) }, "setting a nil value should panic") } func TestPrefixStoreIterate(t *testing.T) { db := dbm.NewMemDB() - baseStore := dbStoreAdapter{db} + baseStore := dbadapter.Store{db} prefix := []byte("test") - prefixStore := baseStore.Prefix(prefix) + prefixStore := NewStore(baseStore, prefix) setRandomKVPairs(t, prefixStore) - bIter := sdk.KVStorePrefixIterator(baseStore, prefix) - pIter := sdk.KVStorePrefixIterator(prefixStore, nil) + bIter := types.KVStorePrefixIterator(baseStore, prefix) + pIter := types.KVStorePrefixIterator(prefixStore, nil) for bIter.Valid() && pIter.Valid() { require.Equal(t, bIter.Key(), append(prefix, pIter.Key()...)) @@ -150,11 +148,11 @@ func TestCloneAppend(t *testing.T) { func TestPrefixStoreIteratorEdgeCase(t *testing.T) { db := dbm.NewMemDB() - baseStore := dbStoreAdapter{db} + baseStore := dbadapter.Store{db} // overflow in cpIncr prefix := []byte{0xAA, 0xFF, 0xFF} - prefixStore := baseStore.Prefix(prefix) + prefixStore := NewStore(baseStore, prefix) // ascending order baseStore.Set([]byte{0xAA, 0xFF, 0xFE}, []byte{}) @@ -180,11 +178,11 @@ func TestPrefixStoreIteratorEdgeCase(t *testing.T) { func TestPrefixStoreReverseIteratorEdgeCase(t *testing.T) { db := dbm.NewMemDB() - baseStore := dbStoreAdapter{db} + baseStore := dbadapter.Store{db} // overflow in cpIncr prefix := []byte{0xAA, 0xFF, 0xFF} - prefixStore := baseStore.Prefix(prefix) + prefixStore := NewStore(baseStore, prefix) // descending order baseStore.Set([]byte{0xAB, 0x00, 0x00}, []byte{}) @@ -208,11 +206,11 @@ func TestPrefixStoreReverseIteratorEdgeCase(t *testing.T) { iter.Close() db = dbm.NewMemDB() - baseStore = dbStoreAdapter{db} + baseStore = dbadapter.Store{db} // underflow in cpDecr prefix = []byte{0xAA, 0x00, 0x00} - prefixStore = baseStore.Prefix(prefix) + prefixStore = NewStore(baseStore, prefix) baseStore.Set([]byte{0xAB, 0x00, 0x01, 0x00, 0x00}, []byte{}) baseStore.Set([]byte{0xAB, 0x00, 0x01, 0x00}, []byte{}) @@ -237,9 +235,9 @@ func TestPrefixStoreReverseIteratorEdgeCase(t *testing.T) { // Tests below are ported from https://github.com/tendermint/tendermint/blob/master/libs/db/prefix_db_test.go -func mockStoreWithStuff() sdk.KVStore { +func mockStoreWithStuff() types.KVStore { db := dbm.NewMemDB() - store := dbStoreAdapter{db} + store := dbadapter.Store{db} // Under "key" prefix store.Set(bz("key"), bz("value")) store.Set(bz("key1"), bz("value1")) @@ -253,55 +251,55 @@ func mockStoreWithStuff() sdk.KVStore { return store } -func checkValue(t *testing.T, store sdk.KVStore, key []byte, expected []byte) { +func checkValue(t *testing.T, store types.KVStore, key []byte, expected []byte) { bz := store.Get(key) require.Equal(t, expected, bz) } -func checkValid(t *testing.T, itr sdk.Iterator, expected bool) { +func checkValid(t *testing.T, itr types.Iterator, expected bool) { valid := itr.Valid() require.Equal(t, expected, valid) } -func checkNext(t *testing.T, itr sdk.Iterator, expected bool) { +func checkNext(t *testing.T, itr types.Iterator, expected bool) { itr.Next() valid := itr.Valid() require.Equal(t, expected, valid) } -func checkDomain(t *testing.T, itr sdk.Iterator, start, end []byte) { +func checkDomain(t *testing.T, itr types.Iterator, start, end []byte) { ds, de := itr.Domain() require.Equal(t, start, ds) require.Equal(t, end, de) } -func checkItem(t *testing.T, itr sdk.Iterator, key, value []byte) { +func checkItem(t *testing.T, itr types.Iterator, key, value []byte) { require.Exactly(t, key, itr.Key()) require.Exactly(t, value, itr.Value()) } -func checkInvalid(t *testing.T, itr sdk.Iterator) { +func checkInvalid(t *testing.T, itr types.Iterator) { checkValid(t, itr, false) checkKeyPanics(t, itr) checkValuePanics(t, itr) checkNextPanics(t, itr) } -func checkKeyPanics(t *testing.T, itr sdk.Iterator) { +func checkKeyPanics(t *testing.T, itr types.Iterator) { require.Panics(t, func() { itr.Key() }) } -func checkValuePanics(t *testing.T, itr sdk.Iterator) { +func checkValuePanics(t *testing.T, itr types.Iterator) { require.Panics(t, func() { itr.Value() }) } -func checkNextPanics(t *testing.T, itr sdk.Iterator) { +func checkNextPanics(t *testing.T, itr types.Iterator) { require.Panics(t, func() { itr.Next() }) } func TestPrefixDBSimple(t *testing.T) { store := mockStoreWithStuff() - pstore := store.Prefix(bz("key")) + pstore := NewStore(store, bz("key")) checkValue(t, pstore, bz("key"), nil) checkValue(t, pstore, bz(""), bz("value")) @@ -319,7 +317,7 @@ func TestPrefixDBSimple(t *testing.T) { func TestPrefixDBIterator1(t *testing.T) { store := mockStoreWithStuff() - pstore := store.Prefix(bz("key")) + pstore := NewStore(store, bz("key")) itr := pstore.Iterator(nil, nil) checkDomain(t, itr, nil, nil) @@ -337,7 +335,7 @@ func TestPrefixDBIterator1(t *testing.T) { func TestPrefixDBIterator2(t *testing.T) { store := mockStoreWithStuff() - pstore := store.Prefix(bz("key")) + pstore := NewStore(store, bz("key")) itr := pstore.Iterator(nil, bz("")) checkDomain(t, itr, nil, bz("")) @@ -347,7 +345,7 @@ func TestPrefixDBIterator2(t *testing.T) { func TestPrefixDBIterator3(t *testing.T) { store := mockStoreWithStuff() - pstore := store.Prefix(bz("key")) + pstore := NewStore(store, bz("key")) itr := pstore.Iterator(bz(""), nil) checkDomain(t, itr, bz(""), nil) @@ -365,7 +363,7 @@ func TestPrefixDBIterator3(t *testing.T) { func TestPrefixDBIterator4(t *testing.T) { store := mockStoreWithStuff() - pstore := store.Prefix(bz("key")) + pstore := NewStore(store, bz("key")) itr := pstore.Iterator(bz(""), bz("")) checkDomain(t, itr, bz(""), bz("")) @@ -375,7 +373,7 @@ func TestPrefixDBIterator4(t *testing.T) { func TestPrefixDBReverseIterator1(t *testing.T) { store := mockStoreWithStuff() - pstore := store.Prefix(bz("key")) + pstore := NewStore(store, bz("key")) itr := pstore.ReverseIterator(nil, nil) checkDomain(t, itr, nil, nil) @@ -393,7 +391,7 @@ func TestPrefixDBReverseIterator1(t *testing.T) { func TestPrefixDBReverseIterator2(t *testing.T) { store := mockStoreWithStuff() - pstore := store.Prefix(bz("key")) + pstore := NewStore(store, bz("key")) itr := pstore.ReverseIterator(bz(""), nil) checkDomain(t, itr, bz(""), nil) @@ -411,7 +409,7 @@ func TestPrefixDBReverseIterator2(t *testing.T) { func TestPrefixDBReverseIterator3(t *testing.T) { store := mockStoreWithStuff() - pstore := store.Prefix(bz("key")) + pstore := NewStore(store, bz("key")) itr := pstore.ReverseIterator(nil, bz("")) checkDomain(t, itr, nil, bz("")) @@ -421,7 +419,7 @@ func TestPrefixDBReverseIterator3(t *testing.T) { func TestPrefixDBReverseIterator4(t *testing.T) { store := mockStoreWithStuff() - pstore := store.Prefix(bz("key")) + pstore := NewStore(store, bz("key")) itr := pstore.ReverseIterator(bz(""), bz("")) checkInvalid(t, itr) diff --git a/store/pruning.go b/store/pruning.go deleted file mode 100644 index 9a7aeb4d29e6..000000000000 --- a/store/pruning.go +++ /dev/null @@ -1,29 +0,0 @@ -package store - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// default pruning strategies -var ( - // PruneEverything means all saved states will be deleted, storing only the current state - PruneEverything = sdk.NewPruningOptions(0, 0) - // PruneNothing means all historic states will be saved, nothing will be deleted - PruneNothing = sdk.NewPruningOptions(0, 1) - // PruneSyncable means only those states not needed for state syncing will be deleted (keeps last 100 + every 10000th) - PruneSyncable = sdk.NewPruningOptions(100, 10000) -) - -func NewPruningOptions(strategy string) (opt PruningOptions) { - switch strategy { - case "nothing": - opt = PruneNothing - case "everything": - opt = PruneEverything - case "syncable": - opt = PruneSyncable - default: - opt = PruneSyncable - } - return -} diff --git a/store/queue.go b/store/queue/queue.go similarity index 95% rename from store/queue.go rename to store/queue/queue.go index f41ecb7d7a19..0f65d0375ba3 100644 --- a/store/queue.go +++ b/store/queue/queue.go @@ -1,8 +1,12 @@ package store +// TODO: make it independent from list +/* import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/cosmos-sdk/store/list" ) // Key for the top element position in the queue @@ -13,7 +17,7 @@ func TopKey() []byte { // Queue is a List wrapper that provides queue-like functions // It panics when the element type cannot be (un/)marshalled by the codec type Queue struct { - List List + List list.List } // NewQueue constructs new Queue @@ -86,3 +90,4 @@ func (m Queue) Flush(ptr interface{}, fn func() bool) { } m.setTop(i) } +*/ diff --git a/store/queue_test.go b/store/queue_test.go deleted file mode 100644 index 58e96f56a19e..000000000000 --- a/store/queue_test.go +++ /dev/null @@ -1,102 +0,0 @@ -package store - -import ( - "testing" - - "github.com/stretchr/testify/require" - - dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/libs/log" - - abci "github.com/tendermint/tendermint/abci/types" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -type S struct { - I uint64 - B bool -} - -func defaultComponents(key sdk.StoreKey) (sdk.Context, *codec.Codec) { - db := dbm.NewMemDB() - cms := NewCommitMultiStore(db) - cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) - cms.LoadLatestVersion() - ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger()) - cdc := codec.New() - return ctx, cdc -} - -func TestQueue(t *testing.T) { - key := sdk.NewKVStoreKey("test") - ctx, cdc := defaultComponents(key) - store := ctx.KVStore(key) - - qm := NewQueue(cdc, store) - - val := S{1, true} - var res S - - qm.Push(val) - qm.Peek(&res) - require.Equal(t, val, res) - - qm.Pop() - empty := qm.IsEmpty() - - require.True(t, empty) - require.NotNil(t, qm.Peek(&res)) - - qm.Push(S{1, true}) - qm.Push(S{2, true}) - qm.Push(S{3, true}) - qm.Flush(&res, func() (brk bool) { - if res.I == 3 { - brk = true - } - return - }) - - require.False(t, qm.IsEmpty()) - - qm.Pop() - require.True(t, qm.IsEmpty()) -} - -func TestKeys(t *testing.T) { - key := sdk.NewKVStoreKey("test") - ctx, cdc := defaultComponents(key) - store := ctx.KVStore(key) - queue := NewQueue(cdc, store) - - for i := 0; i < 10; i++ { - queue.Push(i) - } - - var len uint64 - var top uint64 - var expected int - var actual int - - // Checking keys.LengthKey - err := cdc.UnmarshalBinaryLengthPrefixed(store.Get(LengthKey()), &len) - require.Nil(t, err) - require.Equal(t, len, queue.List.Len()) - - // Checking keys.ElemKey - for i := 0; i < 10; i++ { - queue.List.Get(uint64(i), &expected) - bz := store.Get(ElemKey(uint64(i))) - err = cdc.UnmarshalBinaryLengthPrefixed(bz, &actual) - require.Nil(t, err) - require.Equal(t, expected, actual) - } - - queue.Pop() - - err = cdc.UnmarshalBinaryLengthPrefixed(store.Get(TopKey()), &top) - require.Nil(t, err) - require.Equal(t, top, queue.getTop()) -} diff --git a/store/codec.go b/store/reexport.go similarity index 72% rename from store/codec.go rename to store/reexport.go index 181f12e511b7..913a8c55a8b3 100644 --- a/store/codec.go +++ b/store/reexport.go @@ -1,7 +1,8 @@ package store import ( - "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/store/types" + stypes "github.com/cosmos/cosmos-sdk/store/types" ) // Import cosmos-sdk/types/store.go for convenience. @@ -26,7 +27,14 @@ type ( StoreType = types.StoreType Queryable = types.Queryable TraceContext = types.TraceContext - Gas = types.Gas + Gas = stypes.Gas GasMeter = types.GasMeter - GasConfig = types.GasConfig + GasConfig = stypes.GasConfig +) + +// nolint - reexport +var ( + PruneNothing = types.PruneNothing + PruneEverything = types.PruneEverything + PruneSyncable = types.PruneSyncable ) diff --git a/store/rootmulti/dbadapter.go b/store/rootmulti/dbadapter.go new file mode 100644 index 000000000000..cda6e62ba721 --- /dev/null +++ b/store/rootmulti/dbadapter.go @@ -0,0 +1,33 @@ +package rootmulti + +import ( + "github.com/cosmos/cosmos-sdk/store/dbadapter" + "github.com/cosmos/cosmos-sdk/store/types" +) + +var commithash = []byte("FAKE_HASH") + +//---------------------------------------- +// commitDBStoreWrapper should only be used for simulation/debugging, +// as it doesn't compute any commit hash, and it cannot load older state. + +// Wrapper type for dbm.Db with implementation of KVStore +type commitDBStoreAdapter struct { + dbadapter.Store +} + +func (cdsa commitDBStoreAdapter) Commit() types.CommitID { + return types.CommitID{ + Version: -1, + Hash: commithash, + } +} + +func (cdsa commitDBStoreAdapter) LastCommitID() types.CommitID { + return types.CommitID{ + Version: -1, + Hash: commithash, + } +} + +func (cdsa commitDBStoreAdapter) SetPruning(_ types.PruningOptions) {} diff --git a/store/multistoreproof.go b/store/rootmulti/proof.go similarity index 99% rename from store/multistoreproof.go rename to store/rootmulti/proof.go index 96f0a48373ea..6657a6007a66 100644 --- a/store/multistoreproof.go +++ b/store/rootmulti/proof.go @@ -1,4 +1,4 @@ -package store +package rootmulti import ( "bytes" diff --git a/store/multistoreproof_test.go b/store/rootmulti/proof_test.go similarity index 84% rename from store/multistoreproof_test.go rename to store/rootmulti/proof_test.go index 3d70451e7273..885d0ddb4eb4 100644 --- a/store/multistoreproof_test.go +++ b/store/rootmulti/proof_test.go @@ -1,20 +1,22 @@ -package store +package rootmulti import ( "testing" + "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" dbm "github.com/tendermint/tendermint/libs/db" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/store/iavl" + stypes "github.com/cosmos/cosmos-sdk/store/types" ) func TestVerifyIAVLStoreQueryProof(t *testing.T) { // Create main tree for testing. db := dbm.NewMemDB() - iStore, err := LoadIAVLStore(db, CommitID{}, PruneNothing) - store := iStore.(*iavlStore) + iStore, err := iavl.LoadStore(db, types.CommitID{}, stypes.PruneNothing) + store := iStore.(*iavl.Store) require.Nil(t, err) store.Set([]byte("MYKEY"), []byte("MYVALUE")) cid := store.Commit() @@ -56,13 +58,13 @@ func TestVerifyIAVLStoreQueryProof(t *testing.T) { func TestVerifyMultiStoreQueryProof(t *testing.T) { // Create main tree for testing. db := dbm.NewMemDB() - store := NewCommitMultiStore(db) - iavlStoreKey := sdk.NewKVStoreKey("iavlStoreKey") + store := NewStore(db) + iavlStoreKey := types.NewKVStoreKey("iavlStoreKey") - store.MountStoreWithDB(iavlStoreKey, sdk.StoreTypeIAVL, nil) + store.MountStoreWithDB(iavlStoreKey, types.StoreTypeIAVL, nil) store.LoadVersion(0) - iavlStore := store.GetCommitStore(iavlStoreKey).(*iavlStore) + iavlStore := store.GetCommitStore(iavlStoreKey).(*iavl.Store) iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE")) cid := store.Commit() @@ -111,10 +113,10 @@ func TestVerifyMultiStoreQueryProof(t *testing.T) { func TestVerifyMultiStoreQueryProofEmptyStore(t *testing.T) { // Create main tree for testing. db := dbm.NewMemDB() - store := NewCommitMultiStore(db) - iavlStoreKey := sdk.NewKVStoreKey("iavlStoreKey") + store := NewStore(db) + iavlStoreKey := types.NewKVStoreKey("iavlStoreKey") - store.MountStoreWithDB(iavlStoreKey, sdk.StoreTypeIAVL, nil) + store.MountStoreWithDB(iavlStoreKey, types.StoreTypeIAVL, nil) store.LoadVersion(0) cid := store.Commit() // Commit with empty iavl store. @@ -140,13 +142,13 @@ func TestVerifyMultiStoreQueryProofEmptyStore(t *testing.T) { func TestVerifyMultiStoreQueryProofAbsence(t *testing.T) { // Create main tree for testing. db := dbm.NewMemDB() - store := NewCommitMultiStore(db) - iavlStoreKey := sdk.NewKVStoreKey("iavlStoreKey") + store := NewStore(db) + iavlStoreKey := types.NewKVStoreKey("iavlStoreKey") - store.MountStoreWithDB(iavlStoreKey, sdk.StoreTypeIAVL, nil) + store.MountStoreWithDB(iavlStoreKey, types.StoreTypeIAVL, nil) store.LoadVersion(0) - iavlStore := store.GetCommitStore(iavlStoreKey).(*iavlStore) + iavlStore := store.GetCommitStore(iavlStoreKey).(*iavl.Store) iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE")) cid := store.Commit() // Commit with empty iavl store. diff --git a/store/rootmultistore.go b/store/rootmulti/store.go similarity index 67% rename from store/rootmultistore.go rename to store/rootmulti/store.go index fa576d3dcc6a..8be98a1d8c52 100644 --- a/store/rootmultistore.go +++ b/store/rootmulti/store.go @@ -1,4 +1,4 @@ -package store +package rootmulti import ( "fmt" @@ -10,7 +10,12 @@ import ( "github.com/tendermint/tendermint/crypto/tmhash" 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/dbadapter" + "github.com/cosmos/cosmos-sdk/store/iavl" + "github.com/cosmos/cosmos-sdk/store/tracekv" + "github.com/cosmos/cosmos-sdk/store/transient" + "github.com/cosmos/cosmos-sdk/types" ) const ( @@ -18,36 +23,36 @@ const ( commitInfoKeyFmt = "s/%d" // s/ ) -// rootMultiStore is composed of many CommitStores. Name contrasts with +// Store is composed of many CommitStores. Name contrasts with // cacheMultiStore which is for cache-wrapping other MultiStores. It implements // the CommitMultiStore interface. -type rootMultiStore struct { +type Store struct { db dbm.DB - lastCommitID CommitID - pruningOpts sdk.PruningOptions - storesParams map[StoreKey]storeParams - stores map[StoreKey]CommitStore - keysByName map[string]StoreKey + lastCommitID types.CommitID + pruningOpts types.PruningOptions + storesParams map[types.StoreKey]storeParams + stores map[types.StoreKey]types.CommitStore + keysByName map[string]types.StoreKey traceWriter io.Writer - traceContext TraceContext + traceContext types.TraceContext } -var _ CommitMultiStore = (*rootMultiStore)(nil) -var _ Queryable = (*rootMultiStore)(nil) +var _ types.CommitMultiStore = (*Store)(nil) +var _ types.Queryable = (*Store)(nil) // nolint -func NewCommitMultiStore(db dbm.DB) *rootMultiStore { - return &rootMultiStore{ +func NewStore(db dbm.DB) *Store { + return &Store{ db: db, - storesParams: make(map[StoreKey]storeParams), - stores: make(map[StoreKey]CommitStore), - keysByName: make(map[string]StoreKey), + storesParams: make(map[types.StoreKey]storeParams), + stores: make(map[types.StoreKey]types.CommitStore), + keysByName: make(map[string]types.StoreKey), } } // Implements CommitMultiStore -func (rs *rootMultiStore) SetPruning(pruningOpts sdk.PruningOptions) { +func (rs *Store) SetPruning(pruningOpts types.PruningOptions) { rs.pruningOpts = pruningOpts for _, substore := range rs.stores { substore.SetPruning(pruningOpts) @@ -55,20 +60,20 @@ func (rs *rootMultiStore) SetPruning(pruningOpts sdk.PruningOptions) { } // Implements Store. -func (rs *rootMultiStore) GetStoreType() StoreType { - return sdk.StoreTypeMulti +func (rs *Store) GetStoreType() types.StoreType { + return types.StoreTypeMulti } // Implements CommitMultiStore. -func (rs *rootMultiStore) MountStoreWithDB(key StoreKey, typ StoreType, db dbm.DB) { +func (rs *Store) MountStoreWithDB(key types.StoreKey, typ types.StoreType, db dbm.DB) { if key == nil { panic("MountIAVLStore() key cannot be nil") } if _, ok := rs.storesParams[key]; ok { - panic(fmt.Sprintf("rootMultiStore duplicate store key %v", key)) + panic(fmt.Sprintf("Store duplicate store key %v", key)) } if _, ok := rs.keysByName[key.Name()]; ok { - panic(fmt.Sprintf("rootMultiStore duplicate store key name %v", key)) + panic(fmt.Sprintf("Store duplicate store key name %v", key)) } rs.storesParams[key] = storeParams{ key: key, @@ -79,36 +84,36 @@ func (rs *rootMultiStore) MountStoreWithDB(key StoreKey, typ StoreType, db dbm.D } // Implements CommitMultiStore. -func (rs *rootMultiStore) GetCommitStore(key StoreKey) CommitStore { +func (rs *Store) GetCommitStore(key types.StoreKey) types.CommitStore { return rs.stores[key] } // Implements CommitMultiStore. -func (rs *rootMultiStore) GetCommitKVStore(key StoreKey) CommitKVStore { - return rs.stores[key].(CommitKVStore) +func (rs *Store) GetCommitKVStore(key types.StoreKey) types.CommitKVStore { + return rs.stores[key].(types.CommitKVStore) } // Implements CommitMultiStore. -func (rs *rootMultiStore) LoadLatestVersion() error { +func (rs *Store) LoadLatestVersion() error { ver := getLatestVersion(rs.db) return rs.LoadVersion(ver) } // Implements CommitMultiStore. -func (rs *rootMultiStore) LoadVersion(ver int64) error { +func (rs *Store) LoadVersion(ver int64) error { // Special logic for version 0 if ver == 0 { for key, storeParams := range rs.storesParams { - id := CommitID{} + id := types.CommitID{} store, err := rs.loadCommitStoreFromParams(key, id, storeParams) if err != nil { - return fmt.Errorf("failed to load rootMultiStore: %v", err) + return fmt.Errorf("failed to load Store: %v", err) } rs.stores[key] = store } - rs.lastCommitID = CommitID{} + rs.lastCommitID = types.CommitID{} return nil } // Otherwise, version is 1 or greater @@ -120,15 +125,15 @@ func (rs *rootMultiStore) LoadVersion(ver int64) error { } // Convert StoreInfos slice to map - infos := make(map[StoreKey]storeInfo) + infos := make(map[types.StoreKey]storeInfo) for _, storeInfo := range cInfo.StoreInfos { infos[rs.nameToKey(storeInfo.Name)] = storeInfo } // Load each Store - var newStores = make(map[StoreKey]CommitStore) + var newStores = make(map[types.StoreKey]types.CommitStore) for key, storeParams := range rs.storesParams { - var id CommitID + var id types.CommitID info, ok := infos[key] if ok { id = info.Core.CommitID @@ -136,7 +141,7 @@ func (rs *rootMultiStore) LoadVersion(ver int64) error { store, err := rs.loadCommitStoreFromParams(key, id, storeParams) if err != nil { - return fmt.Errorf("failed to load rootMultiStore: %v", err) + return fmt.Errorf("failed to load Store: %v", err) } newStores[key] = store } @@ -147,18 +152,18 @@ func (rs *rootMultiStore) LoadVersion(ver int64) error { return nil } -// WithTracer sets the tracer for the MultiStore that the underlying +// SetTracer sets the tracer for the MultiStore that the underlying // stores will utilize to trace operations. A MultiStore is returned. -func (rs *rootMultiStore) WithTracer(w io.Writer) MultiStore { +func (rs *Store) SetTracer(w io.Writer) types.MultiStore { rs.traceWriter = w return rs } -// WithTracingContext updates the tracing context for the MultiStore by merging +// SetTracingContext updates the tracing context for the MultiStore by merging // the given context with the existing context by key. Any existing keys will // be overwritten. It is implied that the caller should update the context when // necessary between tracing operations. It returns a modified MultiStore. -func (rs *rootMultiStore) WithTracingContext(tc TraceContext) MultiStore { +func (rs *Store) SetTracingContext(tc types.TraceContext) types.MultiStore { if rs.traceContext != nil { for k, v := range tc { rs.traceContext[k] = v @@ -171,26 +176,20 @@ func (rs *rootMultiStore) WithTracingContext(tc TraceContext) MultiStore { } // TracingEnabled returns if tracing is enabled for the MultiStore. -func (rs *rootMultiStore) TracingEnabled() bool { +func (rs *Store) TracingEnabled() bool { return rs.traceWriter != nil } -// ResetTraceContext resets the current tracing context. -func (rs *rootMultiStore) ResetTraceContext() MultiStore { - rs.traceContext = nil - return rs -} - //---------------------------------------- // +CommitStore // Implements Committer/CommitStore. -func (rs *rootMultiStore) LastCommitID() CommitID { +func (rs *Store) LastCommitID() types.CommitID { return rs.lastCommitID } // Implements Committer/CommitStore. -func (rs *rootMultiStore) Commit() CommitID { +func (rs *Store) Commit() types.CommitID { // Commit stores. version := rs.lastCommitID.Version + 1 @@ -203,7 +202,7 @@ func (rs *rootMultiStore) Commit() CommitID { batch.Write() // Prepare for next version. - commitID := CommitID{ + commitID := types.CommitID{ Version: version, Hash: commitInfo.Hash(), } @@ -212,12 +211,12 @@ func (rs *rootMultiStore) Commit() CommitID { } // Implements CacheWrapper/Store/CommitStore. -func (rs *rootMultiStore) CacheWrap() CacheWrap { - return rs.CacheMultiStore().(CacheWrap) +func (rs *Store) CacheWrap() types.CacheWrap { + return rs.CacheMultiStore().(types.CacheWrap) } // CacheWrapWithTrace implements the CacheWrapper interface. -func (rs *rootMultiStore) CacheWrapWithTrace(_ io.Writer, _ TraceContext) CacheWrap { +func (rs *Store) CacheWrapWithTrace(_ io.Writer, _ types.TraceContext) types.CacheWrap { return rs.CacheWrap() } @@ -225,13 +224,17 @@ func (rs *rootMultiStore) CacheWrapWithTrace(_ io.Writer, _ TraceContext) CacheW // +MultiStore // Implements MultiStore. -func (rs *rootMultiStore) CacheMultiStore() CacheMultiStore { - return newCacheMultiStoreFromRMS(rs) +func (rs *Store) CacheMultiStore() types.CacheMultiStore { + stores := make(map[types.StoreKey]types.CacheWrapper) + for k, v := range rs.stores { + stores[k] = v + } + return cachemulti.NewStore(rs.db, stores, rs.keysByName, rs.traceWriter, rs.traceContext) } // Implements MultiStore. // If the store does not exist, panics. -func (rs *rootMultiStore) GetStore(key StoreKey) Store { +func (rs *Store) GetStore(key types.StoreKey) types.Store { store := rs.stores[key] if store == nil { panic("Could not load store " + key.String()) @@ -240,14 +243,14 @@ func (rs *rootMultiStore) GetStore(key StoreKey) Store { } // GetKVStore implements the MultiStore interface. If tracing is enabled on the -// rootMultiStore, a wrapped TraceKVStore will be returned with the given +// Store, a wrapped TraceKVStore will be returned with the given // tracer, otherwise, the original KVStore will be returned. // If the store does not exist, panics. -func (rs *rootMultiStore) GetKVStore(key StoreKey) KVStore { - store := rs.stores[key].(KVStore) +func (rs *Store) GetKVStore(key types.StoreKey) types.KVStore { + store := rs.stores[key].(types.KVStore) if rs.TracingEnabled() { - store = NewTraceKVStore(store, rs.traceWriter, rs.traceContext) + store = tracekv.NewStore(store, rs.traceWriter, rs.traceContext) } return store @@ -260,7 +263,7 @@ func (rs *rootMultiStore) GetKVStore(key StoreKey) KVStore { // This is not exposed to the extensions (which will need the // StoreKey), but is useful in main, and particularly app.Query, // in order to convert human strings into CommitStores. -func (rs *rootMultiStore) getStoreByName(name string) Store { +func (rs *Store) getStoreByName(name string) types.Store { key := rs.keysByName[name] if key == nil { return nil @@ -274,7 +277,7 @@ func (rs *rootMultiStore) getStoreByName(name string) Store { // modified to remove the substore prefix. // Ie. `req.Path` here is `//`, and trimmed to `/` for the substore. // TODO: add proof for `multistore -> substore`. -func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery { +func (rs *Store) Query(req abci.RequestQuery) abci.ResponseQuery { // Query just routes this to a substore. path := req.Path storeName, subpath, err := parsePath(path) @@ -285,12 +288,12 @@ func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery { store := rs.getStoreByName(storeName) if store == nil { msg := fmt.Sprintf("no such store: %s", storeName) - return sdk.ErrUnknownRequest(msg).QueryResult() + return types.ErrUnknownRequest(msg).QueryResult() } - queryable, ok := store.(Queryable) + queryable, ok := store.(types.Queryable) if !ok { msg := fmt.Sprintf("store %s doesn't support queries", storeName) - return sdk.ErrUnknownRequest(msg).QueryResult() + return types.ErrUnknownRequest(msg).QueryResult() } // trim the path and make the query @@ -302,12 +305,12 @@ func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery { } if res.Proof == nil || len(res.Proof.Ops) == 0 { - return sdk.ErrInternal("substore proof was nil/empty when it should never be").QueryResult() + return types.ErrInternal("substore proof was nil/empty when it should never be").QueryResult() } commitInfo, errMsg := getCommitInfo(rs.db, res.Height) if errMsg != nil { - return sdk.ErrInternal(errMsg.Error()).QueryResult() + return types.ErrInternal(errMsg.Error()).QueryResult() } // Restore origin path and append proof op. @@ -324,9 +327,9 @@ func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery { // 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 sdk.Error) { +func parsePath(path string) (storeName string, subpath string, err types.Error) { if !strings.HasPrefix(path, "/") { - err = sdk.ErrUnknownRequest(fmt.Sprintf("invalid path: %s", path)) + err = types.ErrUnknownRequest(fmt.Sprintf("invalid path: %s", path)) return } @@ -342,7 +345,7 @@ func parsePath(path string) (storeName string, subpath string, err sdk.Error) { //---------------------------------------- -func (rs *rootMultiStore) loadCommitStoreFromParams(key sdk.StoreKey, id CommitID, params storeParams) (store CommitStore, err error) { +func (rs *Store) loadCommitStoreFromParams(key types.StoreKey, id types.CommitID, params storeParams) (store types.CommitStore, err error) { var db dbm.DB if params.db != nil { db = dbm.NewPrefixDB(params.db, []byte("s/_/")) @@ -350,30 +353,30 @@ func (rs *rootMultiStore) loadCommitStoreFromParams(key sdk.StoreKey, id CommitI db = dbm.NewPrefixDB(rs.db, []byte("s/k:"+params.key.Name()+"/")) } switch params.typ { - case sdk.StoreTypeMulti: + case types.StoreTypeMulti: panic("recursive MultiStores not yet supported") // TODO: id? // return NewCommitMultiStore(db, id) - case sdk.StoreTypeIAVL: - store, err = LoadIAVLStore(db, id, rs.pruningOpts) + case types.StoreTypeIAVL: + store, err = iavl.LoadStore(db, id, rs.pruningOpts) return - case sdk.StoreTypeDB: - store = commitDBStoreAdapter{dbStoreAdapter{db}} + case types.StoreTypeDB: + store = commitDBStoreAdapter{dbadapter.Store{db}} return - case sdk.StoreTypeTransient: - _, ok := key.(*sdk.TransientStoreKey) + case types.StoreTypeTransient: + _, ok := key.(*types.TransientStoreKey) if !ok { err = fmt.Errorf("invalid StoreKey for StoreTypeTransient: %s", key.String()) return } - store = newTransientStore() + store = transient.NewStore() return default: panic(fmt.Sprintf("unrecognized store type %v", params.typ)) } } -func (rs *rootMultiStore) nameToKey(name string) StoreKey { +func (rs *Store) nameToKey(name string) types.StoreKey { for key := range rs.storesParams { if key.Name() == name { return key @@ -386,9 +389,9 @@ func (rs *rootMultiStore) nameToKey(name string) StoreKey { // storeParams type storeParams struct { - key StoreKey + key types.StoreKey db dbm.DB - typ StoreType + typ types.StoreType } //---------------------------------------- @@ -415,8 +418,8 @@ func (ci commitInfo) Hash() []byte { return merkle.SimpleHashFromMap(m) } -func (ci commitInfo) CommitID() CommitID { - return CommitID{ +func (ci commitInfo) CommitID() types.CommitID { + return types.CommitID{ Version: ci.Version, Hash: ci.Hash(), } @@ -426,7 +429,7 @@ func (ci commitInfo) CommitID() CommitID { // storeInfo // storeInfo contains the name and core reference for an -// underlying store. It is the leaf of the rootMultiStores top +// underlying store. It is the leaf of the Stores top // level simple merkle tree. type storeInfo struct { Name string @@ -435,7 +438,7 @@ type storeInfo struct { type storeCore struct { // StoreType StoreType - CommitID CommitID + CommitID types.CommitID // ... maybe add more state } @@ -480,14 +483,14 @@ func setLatestVersion(batch dbm.Batch, version int64) { } // Commits each store and returns a new commitInfo. -func commitStores(version int64, storeMap map[StoreKey]CommitStore) commitInfo { +func commitStores(version int64, storeMap map[types.StoreKey]types.CommitStore) commitInfo { storeInfos := make([]storeInfo, 0, len(storeMap)) for key, store := range storeMap { // Commit commitID := store.Commit() - if store.GetStoreType() == sdk.StoreTypeTransient { + if store.GetStoreType() == types.StoreTypeTransient { continue } @@ -513,14 +516,14 @@ func getCommitInfo(db dbm.DB, ver int64) (commitInfo, error) { cInfoKey := fmt.Sprintf(commitInfoKeyFmt, ver) cInfoBytes := db.Get([]byte(cInfoKey)) if cInfoBytes == nil { - return commitInfo{}, fmt.Errorf("failed to get rootMultiStore: no data") + return commitInfo{}, fmt.Errorf("failed to get Store: no data") } var cInfo commitInfo err := cdc.UnmarshalBinaryLengthPrefixed(cInfoBytes, &cInfo) if err != nil { - return commitInfo{}, fmt.Errorf("failed to get rootMultiStore: %v", err) + return commitInfo{}, fmt.Errorf("failed to get Store: %v", err) } return cInfo, nil diff --git a/store/rootmultistore_test.go b/store/rootmulti/store_test.go similarity index 71% rename from store/rootmultistore_test.go rename to store/rootmulti/store_test.go index 10f0956562ba..75aed91b6c4b 100644 --- a/store/rootmultistore_test.go +++ b/store/rootmulti/store_test.go @@ -1,4 +1,4 @@ -package store +package rootmulti import ( "testing" @@ -8,32 +8,33 @@ import ( "github.com/tendermint/tendermint/crypto/merkle" dbm "github.com/tendermint/tendermint/libs/db" - sdk "github.com/cosmos/cosmos-sdk/types" + stypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/types" ) const useDebugDB = false func TestStoreType(t *testing.T) { db := dbm.NewMemDB() - store := NewCommitMultiStore(db) + store := NewStore(db) store.MountStoreWithDB( - sdk.NewKVStoreKey("store1"), sdk.StoreTypeIAVL, db) + types.NewKVStoreKey("store1"), types.StoreTypeIAVL, db) } func TestStoreMount(t *testing.T) { db := dbm.NewMemDB() - store := NewCommitMultiStore(db) + store := NewStore(db) - key1 := sdk.NewKVStoreKey("store1") - key2 := sdk.NewKVStoreKey("store2") - dup1 := sdk.NewKVStoreKey("store1") + key1 := types.NewKVStoreKey("store1") + key2 := types.NewKVStoreKey("store2") + dup1 := types.NewKVStoreKey("store1") - require.NotPanics(t, func() { store.MountStoreWithDB(key1, sdk.StoreTypeIAVL, db) }) - require.NotPanics(t, func() { store.MountStoreWithDB(key2, sdk.StoreTypeIAVL, db) }) + require.NotPanics(t, func() { store.MountStoreWithDB(key1, types.StoreTypeIAVL, db) }) + require.NotPanics(t, func() { store.MountStoreWithDB(key2, types.StoreTypeIAVL, db) }) - require.Panics(t, func() { store.MountStoreWithDB(key1, sdk.StoreTypeIAVL, db) }) - require.Panics(t, func() { store.MountStoreWithDB(dup1, sdk.StoreTypeIAVL, db) }) + require.Panics(t, func() { store.MountStoreWithDB(key1, types.StoreTypeIAVL, db) }) + require.Panics(t, func() { store.MountStoreWithDB(dup1, types.StoreTypeIAVL, db) }) } func TestMultistoreCommitLoad(t *testing.T) { @@ -46,7 +47,7 @@ func TestMultistoreCommitLoad(t *testing.T) { require.Nil(t, err) // New store has empty last commit. - commitID := CommitID{} + commitID := types.CommitID{} checkStore(t, store, commitID, commitID) // Make sure we can get stores by name. @@ -137,11 +138,11 @@ func TestMultiStoreQuery(t *testing.T) { require.Nil(t, garbage) // Set and commit data in one store. - store1 := multi.getStoreByName("store1").(KVStore) + store1 := multi.getStoreByName("store1").(types.KVStore) store1.Set(k, v) // ... and another. - store2 := multi.getStoreByName("store2").(KVStore) + store2 := multi.getStoreByName("store2").(types.KVStore) store2.Set(k2, v2) // Commit the multistore. @@ -156,69 +157,69 @@ func TestMultiStoreQuery(t *testing.T) { // Test bad path. query := abci.RequestQuery{Path: "/key", Data: k, Height: ver} qres := multi.Query(query) - require.EqualValues(t, sdk.CodeUnknownRequest, qres.Code) - require.EqualValues(t, sdk.CodespaceRoot, qres.Codespace) + require.EqualValues(t, types.CodeUnknownRequest, qres.Code) + require.EqualValues(t, types.CodespaceRoot, qres.Codespace) query.Path = "h897fy32890rf63296r92" qres = multi.Query(query) - require.EqualValues(t, sdk.CodeUnknownRequest, qres.Code) - require.EqualValues(t, sdk.CodespaceRoot, qres.Codespace) + require.EqualValues(t, types.CodeUnknownRequest, qres.Code) + require.EqualValues(t, types.CodespaceRoot, qres.Codespace) // Test invalid store name. query.Path = "/garbage/key" qres = multi.Query(query) - require.EqualValues(t, sdk.CodeUnknownRequest, qres.Code) - require.EqualValues(t, sdk.CodespaceRoot, qres.Codespace) + require.EqualValues(t, types.CodeUnknownRequest, qres.Code) + require.EqualValues(t, types.CodespaceRoot, qres.Codespace) // Test valid query with data. query.Path = "/store1/key" qres = multi.Query(query) - require.EqualValues(t, sdk.CodeOK, qres.Code) + require.EqualValues(t, types.CodeOK, qres.Code) require.Equal(t, v, qres.Value) // Test valid but empty query. query.Path = "/store2/key" query.Prove = true qres = multi.Query(query) - require.EqualValues(t, sdk.CodeOK, qres.Code) + require.EqualValues(t, types.CodeOK, qres.Code) require.Nil(t, qres.Value) // Test store2 data. query.Data = k2 qres = multi.Query(query) - require.EqualValues(t, sdk.CodeOK, qres.Code) + require.EqualValues(t, types.CodeOK, qres.Code) require.Equal(t, v2, qres.Value) } //----------------------------------------------------------------------- // utils -func newMultiStoreWithMounts(db dbm.DB) *rootMultiStore { - store := NewCommitMultiStore(db) - store.pruningOpts = PruneSyncable +func newMultiStoreWithMounts(db dbm.DB) *Store { + store := NewStore(db) + store.pruningOpts = stypes.PruneSyncable store.MountStoreWithDB( - sdk.NewKVStoreKey("store1"), sdk.StoreTypeIAVL, nil) + types.NewKVStoreKey("store1"), types.StoreTypeIAVL, nil) store.MountStoreWithDB( - sdk.NewKVStoreKey("store2"), sdk.StoreTypeIAVL, nil) + types.NewKVStoreKey("store2"), types.StoreTypeIAVL, nil) store.MountStoreWithDB( - sdk.NewKVStoreKey("store3"), sdk.StoreTypeIAVL, nil) + types.NewKVStoreKey("store3"), types.StoreTypeIAVL, nil) return store } -func checkStore(t *testing.T, store *rootMultiStore, expect, got CommitID) { +func checkStore(t *testing.T, store *Store, expect, got types.CommitID) { require.Equal(t, expect, got) require.Equal(t, expect, store.LastCommitID()) } -func getExpectedCommitID(store *rootMultiStore, ver int64) CommitID { - return CommitID{ +func getExpectedCommitID(store *Store, ver int64) types.CommitID { + return types.CommitID{ Version: ver, Hash: hashStores(store.stores), } } -func hashStores(stores map[StoreKey]CommitStore) []byte { +func hashStores(stores map[types.StoreKey]types.CommitStore) []byte { m := make(map[string][]byte, len(stores)) for key, store := range stores { name := key.Name() diff --git a/store/rootmulti/wire.go b/store/rootmulti/wire.go new file mode 100644 index 000000000000..8d6d936160ff --- /dev/null +++ b/store/rootmulti/wire.go @@ -0,0 +1,7 @@ +package rootmulti + +import ( + "github.com/cosmos/cosmos-sdk/codec" +) + +var cdc = codec.New() diff --git a/store/store.go b/store/store.go new file mode 100644 index 000000000000..cb7a0ada8515 --- /dev/null +++ b/store/store.go @@ -0,0 +1,26 @@ +package store + +import ( + dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/cosmos/cosmos-sdk/store/rootmulti" + "github.com/cosmos/cosmos-sdk/store/types" +) + +func NewCommitMultiStore(db dbm.DB) types.CommitMultiStore { + return rootmulti.NewStore(db) +} + +func NewPruningOptionsFromString(strategy string) (opt PruningOptions) { + switch strategy { + case "nothing": + opt = PruneNothing + case "everything": + opt = PruneEverything + case "syncable": + opt = PruneSyncable + default: + opt = PruneSyncable + } + return +} diff --git a/store/tracekvstore.go b/store/tracekv/store.go similarity index 68% rename from store/tracekvstore.go rename to store/tracekv/store.go index d8c34c354542..ee9558ecbe5a 100644 --- a/store/tracekvstore.go +++ b/store/tracekv/store.go @@ -1,4 +1,4 @@ -package store +package tracekv import ( "encoding/base64" @@ -6,7 +6,7 @@ import ( "fmt" "io" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/store/types" ) const ( @@ -18,16 +18,16 @@ const ( ) type ( - // TraceKVStore implements the KVStore interface with tracing enabled. + // Store implements the KVStore interface with tracing enabled. // Operations are traced on each core KVStore call and written to the // underlying io.writer. // // TODO: Should we use a buffered writer and implement Commit on - // TraceKVStore? - TraceKVStore struct { - parent sdk.KVStore + // Store? + Store struct { + parent types.KVStore writer io.Writer - context TraceContext + context types.TraceContext } // operation represents an IO operation @@ -42,15 +42,15 @@ type ( } ) -// NewTraceKVStore returns a reference to a new traceKVStore given a parent +// NewStore returns a reference to a new traceKVStore given a parent // KVStore implementation and a buffered writer. -func NewTraceKVStore(parent sdk.KVStore, writer io.Writer, tc TraceContext) *TraceKVStore { - return &TraceKVStore{parent: parent, writer: writer, context: tc} +func NewStore(parent types.KVStore, writer io.Writer, tc types.TraceContext) *Store { + return &Store{parent: parent, writer: writer, context: tc} } // Get implements the KVStore interface. It traces a read operation and // delegates a Get call to the parent KVStore. -func (tkv *TraceKVStore) Get(key []byte) []byte { +func (tkv *Store) Get(key []byte) []byte { value := tkv.parent.Get(key) writeOperation(tkv.writer, readOp, tkv.context, key, value) @@ -59,50 +59,40 @@ func (tkv *TraceKVStore) Get(key []byte) []byte { // Set implements the KVStore interface. It traces a write operation and // delegates the Set call to the parent KVStore. -func (tkv *TraceKVStore) Set(key []byte, value []byte) { +func (tkv *Store) Set(key []byte, value []byte) { writeOperation(tkv.writer, writeOp, tkv.context, key, value) tkv.parent.Set(key, value) } // Delete implements the KVStore interface. It traces a write operation and // delegates the Delete call to the parent KVStore. -func (tkv *TraceKVStore) Delete(key []byte) { +func (tkv *Store) Delete(key []byte) { writeOperation(tkv.writer, deleteOp, tkv.context, key, nil) tkv.parent.Delete(key) } // Has implements the KVStore interface. It delegates the Has call to the // parent KVStore. -func (tkv *TraceKVStore) Has(key []byte) bool { +func (tkv *Store) Has(key []byte) bool { return tkv.parent.Has(key) } -// Prefix implements the KVStore interface. -func (tkv *TraceKVStore) Prefix(prefix []byte) KVStore { - return prefixStore{tkv, prefix} -} - -// Gas implements the KVStore interface. -func (tkv *TraceKVStore) Gas(meter GasMeter, config GasConfig) KVStore { - return NewGasKVStore(meter, config, tkv.parent) -} - // Iterator implements the KVStore interface. It delegates the Iterator call // the to the parent KVStore. -func (tkv *TraceKVStore) Iterator(start, end []byte) sdk.Iterator { +func (tkv *Store) Iterator(start, end []byte) types.Iterator { return tkv.iterator(start, end, true) } // ReverseIterator implements the KVStore interface. It delegates the // ReverseIterator call the to the parent KVStore. -func (tkv *TraceKVStore) ReverseIterator(start, end []byte) sdk.Iterator { +func (tkv *Store) ReverseIterator(start, end []byte) types.Iterator { return tkv.iterator(start, end, false) } // iterator facilitates iteration over a KVStore. It delegates the necessary // calls to it's parent KVStore. -func (tkv *TraceKVStore) iterator(start, end []byte, ascending bool) sdk.Iterator { - var parent sdk.Iterator +func (tkv *Store) iterator(start, end []byte, ascending bool) types.Iterator { + var parent types.Iterator if ascending { parent = tkv.parent.Iterator(start, end) @@ -114,12 +104,12 @@ func (tkv *TraceKVStore) iterator(start, end []byte, ascending bool) sdk.Iterato } type traceIterator struct { - parent sdk.Iterator + parent types.Iterator writer io.Writer - context TraceContext + context types.TraceContext } -func newTraceIterator(w io.Writer, parent sdk.Iterator, tc TraceContext) sdk.Iterator { +func newTraceIterator(w io.Writer, parent types.Iterator, tc types.TraceContext) types.Iterator { return &traceIterator{writer: w, parent: parent, context: tc} } @@ -161,26 +151,26 @@ func (ti *traceIterator) Close() { // GetStoreType implements the KVStore interface. It returns the underlying // KVStore type. -func (tkv *TraceKVStore) GetStoreType() sdk.StoreType { +func (tkv *Store) GetStoreType() types.StoreType { return tkv.parent.GetStoreType() } -// CacheWrap implements the KVStore interface. It panics as a TraceKVStore +// CacheWrap implements the KVStore interface. It panics as a Store // cannot be cache wrapped. -func (tkv *TraceKVStore) CacheWrap() sdk.CacheWrap { - panic("cannot CacheWrap a TraceKVStore") +func (tkv *Store) CacheWrap() types.CacheWrap { + panic("cannot CacheWrap a Store") } // CacheWrapWithTrace implements the KVStore interface. It panics as a -// TraceKVStore cannot be cache wrapped. -func (tkv *TraceKVStore) CacheWrapWithTrace(_ io.Writer, _ TraceContext) CacheWrap { - panic("cannot CacheWrapWithTrace a TraceKVStore") +// Store cannot be cache wrapped. +func (tkv *Store) CacheWrapWithTrace(_ io.Writer, _ types.TraceContext) types.CacheWrap { + panic("cannot CacheWrapWithTrace a Store") } // writeOperation writes a KVStore operation to the underlying io.Writer as // JSON-encoded data where the key/value pair is base64 encoded. // nolint: errcheck -func writeOperation(w io.Writer, op operation, tc TraceContext, key, value []byte) { +func writeOperation(w io.Writer, op operation, tc types.TraceContext, key, value []byte) { traceOp := traceOperation{ Operation: op, Key: base64.StdEncoding.EncodeToString(key), diff --git a/store/tracekvstore_test.go b/store/tracekv/store_test.go similarity index 89% rename from store/tracekvstore_test.go rename to store/tracekv/store_test.go index 887fcf96e5c9..36d101583d05 100644 --- a/store/tracekvstore_test.go +++ b/store/tracekv/store_test.go @@ -1,22 +1,33 @@ -package store +package tracekv_test import ( "bytes" + "fmt" "io" "testing" "github.com/stretchr/testify/require" dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/cosmos/cosmos-sdk/store/dbadapter" + "github.com/cosmos/cosmos-sdk/store/prefix" + "github.com/cosmos/cosmos-sdk/store/tracekv" + "github.com/cosmos/cosmos-sdk/store/types" ) -var kvPairs = []KVPair{ +func bz(s string) []byte { return []byte(s) } + +func keyFmt(i int) []byte { return bz(fmt.Sprintf("key%0.8d", i)) } +func valFmt(i int) []byte { return bz(fmt.Sprintf("value%0.8d", i)) } + +var kvPairs = []types.KVPair{ {Key: keyFmt(1), Value: valFmt(1)}, {Key: keyFmt(2), Value: valFmt(2)}, {Key: keyFmt(3), Value: valFmt(3)}, } -func newTraceKVStore(w io.Writer) *TraceKVStore { +func newTraceKVStore(w io.Writer) *tracekv.Store { store := newEmptyTraceKVStore(w) for _, kvPair := range kvPairs { @@ -26,11 +37,11 @@ func newTraceKVStore(w io.Writer) *TraceKVStore { return store } -func newEmptyTraceKVStore(w io.Writer) *TraceKVStore { - memDB := dbStoreAdapter{dbm.NewMemDB()} - tc := TraceContext(map[string]interface{}{"blockHeight": 64}) +func newEmptyTraceKVStore(w io.Writer) *tracekv.Store { + memDB := dbadapter.Store{dbm.NewMemDB()} + tc := types.TraceContext(map[string]interface{}{"blockHeight": 64}) - return NewTraceKVStore(memDB, w, tc) + return tracekv.NewStore(memDB, w, tc) } func TestTraceKVStoreGet(t *testing.T) { @@ -263,12 +274,12 @@ func TestTestTraceKVStoreReverseIterator(t *testing.T) { func TestTraceKVStorePrefix(t *testing.T) { store := newEmptyTraceKVStore(nil) - pStore := store.Prefix([]byte("trace_prefix")) - require.IsType(t, prefixStore{}, pStore) + pStore := prefix.NewStore(store, []byte("trace_prefix")) + require.IsType(t, prefix.Store{}, pStore) } func TestTraceKVStoreGetStoreType(t *testing.T) { - memDB := dbStoreAdapter{dbm.NewMemDB()} + memDB := dbadapter.Store{dbm.NewMemDB()} store := newEmptyTraceKVStore(nil) require.Equal(t, memDB.GetStoreType(), store.GetStoreType()) } diff --git a/store/transient/store.go b/store/transient/store.go new file mode 100644 index 000000000000..0a7e945e3e58 --- /dev/null +++ b/store/transient/store.go @@ -0,0 +1,42 @@ +package transient + +import ( + "github.com/cosmos/cosmos-sdk/store/types" + dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/cosmos/cosmos-sdk/store/dbadapter" +) + +var _ types.Committer = (*Store)(nil) +var _ types.KVStore = (*Store)(nil) + +// Store is a wrapper for a MemDB with Commiter implementation +type Store struct { + dbadapter.Store +} + +// Constructs new MemDB adapter +func NewStore() *Store { + return &Store{dbadapter.Store{dbm.NewMemDB()}} +} + +// Implements CommitStore +// Commit cleans up Store. +func (ts *Store) Commit() (id types.CommitID) { + ts.Store = dbadapter.Store{dbm.NewMemDB()} + return +} + +// Implements CommitStore +func (ts *Store) SetPruning(pruning types.PruningOptions) { +} + +// Implements CommitStore +func (ts *Store) LastCommitID() (id types.CommitID) { + return +} + +// Implements Store. +func (ts *Store) GetStoreType() types.StoreType { + return types.StoreTypeTransient +} diff --git a/store/transientstore_test.go b/store/transient/store_test.go similarity index 86% rename from store/transientstore_test.go rename to store/transient/store_test.go index 1c9e98cfaaa1..846c8a3a43ca 100644 --- a/store/transientstore_test.go +++ b/store/transient/store_test.go @@ -1,4 +1,4 @@ -package store +package transient import ( "testing" @@ -9,7 +9,7 @@ import ( var k, v = []byte("hello"), []byte("world") func TestTransientStore(t *testing.T) { - tstore := newTransientStore() + tstore := NewStore() require.Nil(t, tstore.Get(k)) diff --git a/store/transientstore.go b/store/transientstore.go deleted file mode 100644 index 2de1197b977d..000000000000 --- a/store/transientstore.go +++ /dev/null @@ -1,50 +0,0 @@ -package store - -import ( - dbm "github.com/tendermint/tendermint/libs/db" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -var _ KVStore = (*transientStore)(nil) - -// transientStore is a wrapper for a MemDB with Commiter implementation -type transientStore struct { - dbStoreAdapter -} - -// Constructs new MemDB adapter -func newTransientStore() *transientStore { - return &transientStore{dbStoreAdapter{dbm.NewMemDB()}} -} - -// Implements CommitStore -// Commit cleans up transientStore. -func (ts *transientStore) Commit() (id CommitID) { - ts.dbStoreAdapter = dbStoreAdapter{dbm.NewMemDB()} - return -} - -// Implements CommitStore -func (ts *transientStore) SetPruning(opts PruningOptions) { -} - -// Implements CommitStore -func (ts *transientStore) LastCommitID() (id CommitID) { - return -} - -// Implements KVStore -func (ts *transientStore) Prefix(prefix []byte) KVStore { - return prefixStore{ts, prefix} -} - -// Implements KVStore -func (ts *transientStore) Gas(meter GasMeter, config GasConfig) KVStore { - return NewGasKVStore(meter, config, ts) -} - -// Implements Store. -func (ts *transientStore) GetStoreType() StoreType { - return sdk.StoreTypeTransient -} diff --git a/types/gas.go b/store/types/gas.go similarity index 89% rename from types/gas.go rename to store/types/gas.go index 2daecdeb18dd..0acbcd7e2293 100644 --- a/types/gas.go +++ b/store/types/gas.go @@ -1,5 +1,7 @@ package types +import "math" + // Gas consumption descriptors. const ( GasIterNextCostFlatDesc = "IterNextFlat" @@ -69,11 +71,20 @@ func (g *basicGasMeter) GasConsumedToLimit() Gas { return g.consumed } +// addUint64Overflow performs the addition operation on two uint64 integers and +// returns a boolean on whether or not the result overflows. +func addUint64Overflow(a, b uint64) (uint64, bool) { + if math.MaxUint64-a < b { + return 0, true + } + + return a + b, false +} + func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) { var overflow bool - // TODO: Should we set the consumed field after overflow checking? - g.consumed, overflow = AddUint64Overflow(g.consumed, amount) + g.consumed, overflow = addUint64Overflow(g.consumed, amount) if overflow { panic(ErrorGasOverflow{descriptor}) } @@ -81,6 +92,7 @@ func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) { if g.consumed > g.limit { panic(ErrorOutOfGas{descriptor}) } + } func (g *basicGasMeter) IsPastLimit() bool { @@ -116,9 +128,8 @@ func (g *infiniteGasMeter) Limit() Gas { func (g *infiniteGasMeter) ConsumeGas(amount Gas, descriptor string) { var overflow bool - // TODO: Should we set the consumed field after overflow checking? - g.consumed, overflow = AddUint64Overflow(g.consumed, amount) + g.consumed, overflow = addUint64Overflow(g.consumed, amount) if overflow { panic(ErrorGasOverflow{descriptor}) } diff --git a/types/gas_test.go b/store/types/gas_test.go similarity index 70% rename from types/gas_test.go rename to store/types/gas_test.go index 5f862dccdb67..81bb0c901747 100644 --- a/types/gas_test.go +++ b/store/types/gas_test.go @@ -1,6 +1,7 @@ package types import ( + "math" "testing" "github.com/stretchr/testify/require" @@ -43,3 +44,28 @@ func TestGasMeter(t *testing.T) { } } + +func TestAddUint64Overflow(t *testing.T) { + testCases := []struct { + a, b uint64 + result uint64 + overflow bool + }{ + {0, 0, 0, false}, + {100, 100, 200, false}, + {math.MaxUint64 / 2, math.MaxUint64/2 + 1, math.MaxUint64, false}, + {math.MaxUint64 / 2, math.MaxUint64/2 + 2, 0, true}, + } + + for i, tc := range testCases { + res, overflow := addUint64Overflow(tc.a, tc.b) + require.Equal( + t, tc.overflow, overflow, + "invalid overflow result; tc: #%d, a: %d, b: %d", i, tc.a, tc.b, + ) + require.Equal( + t, tc.result, res, + "invalid uint64 result; tc: #%d, a: %d, b: %d", i, tc.a, tc.b, + ) + } +} diff --git a/store/types/pruning.go b/store/types/pruning.go new file mode 100644 index 000000000000..f82023a2c998 --- /dev/null +++ b/store/types/pruning.go @@ -0,0 +1,35 @@ +package types + +// PruningStrategy specifies how old states will be deleted over time where +// keepRecent can be used with keepEvery to create a pruning "strategy". +type PruningOptions struct { + keepRecent int64 + keepEvery int64 +} + +func NewPruningOptions(keepRecent, keepEvery int64) PruningOptions { + return PruningOptions{ + keepRecent: keepRecent, + keepEvery: keepEvery, + } +} + +// How much recent state will be kept. Older state will be deleted. +func (po PruningOptions) KeepRecent() int64 { + return po.keepRecent +} + +// Keeps every N stated, deleting others. +func (po PruningOptions) KeepEvery() int64 { + return po.keepEvery +} + +// default pruning strategies +var ( + // PruneEverything means all saved states will be deleted, storing only the current state + PruneEverything = NewPruningOptions(0, 0) + // PruneNothing means all historic states will be saved, nothing will be deleted + PruneNothing = NewPruningOptions(0, 1) + // PruneSyncable means only those states not needed for state syncing will be deleted (keeps last 100 + every 10000th) + PruneSyncable = NewPruningOptions(100, 10000) +) diff --git a/store/types/store.go b/store/types/store.go new file mode 100644 index 000000000000..db696f688787 --- /dev/null +++ b/store/types/store.go @@ -0,0 +1,274 @@ +package types + +import ( + "fmt" + "io" + + abci "github.com/tendermint/tendermint/abci/types" + cmn "github.com/tendermint/tendermint/libs/common" + dbm "github.com/tendermint/tendermint/libs/db" +) + +type Store interface { //nolint + GetStoreType() StoreType + CacheWrapper +} + +// something that can persist to disk +type Committer interface { + Commit() CommitID + LastCommitID() CommitID + SetPruning(PruningOptions) +} + +// Stores of MultiStore must implement CommitStore. +type CommitStore interface { + Committer + Store +} + +// Queryable allows a Store to expose internal state to the abci.Query +// interface. Multistore can route requests to the proper Store. +// +// This is an optional, but useful extension to any CommitStore +type Queryable interface { + Query(abci.RequestQuery) abci.ResponseQuery +} + +//---------------------------------------- +// MultiStore + +type MultiStore interface { //nolint + Store + + // Cache wrap MultiStore. + // NOTE: Caller should probably not call .Write() on each, but + // call CacheMultiStore.Write(). + CacheMultiStore() CacheMultiStore + + // Convenience for fetching substores. + // If the store does not exist, panics. + GetStore(StoreKey) Store + GetKVStore(StoreKey) KVStore + + // TracingEnabled returns if tracing is enabled for the MultiStore. + TracingEnabled() bool + + // SetTracer sets the tracer for the MultiStore that the underlying + // stores will utilize to trace operations. The modified MultiStore is + // returned. + SetTracer(w io.Writer) MultiStore + + // SetTracingContext sets the tracing context for a MultiStore. It is + // implied that the caller should update the context when necessary between + // tracing operations. The modified MultiStore is returned. + SetTracingContext(TraceContext) MultiStore +} + +// From MultiStore.CacheMultiStore().... +type CacheMultiStore interface { + MultiStore + Write() // Writes operations to underlying KVStore +} + +// A non-cache MultiStore. +type CommitMultiStore interface { + Committer + MultiStore + + // Mount a store of type using the given db. + // If db == nil, the new store will use the CommitMultiStore db. + MountStoreWithDB(key StoreKey, typ StoreType, db dbm.DB) + + // Panics on a nil key. + GetCommitStore(key StoreKey) CommitStore + + // Panics on a nil key. + GetCommitKVStore(key StoreKey) CommitKVStore + + // Load the latest persisted version. Called once after all + // calls to Mount*Store() are complete. + LoadLatestVersion() error + + // Load a specific persisted version. When you load an old + // version, or when the last commit attempt didn't complete, + // the next commit after loading must be idempotent (return the + // same commit id). Otherwise the behavior is undefined. + LoadVersion(ver int64) error +} + +//---------subsp------------------------------- +// KVStore + +// KVStore is a simple interface to get/set data +type KVStore interface { + Store + + // Get returns nil iff key doesn't exist. Panics on nil key. + Get(key []byte) []byte + + // Has checks if a key exists. Panics on nil key. + Has(key []byte) bool + + // Set sets the key. Panics on nil key or value. + Set(key, value []byte) + + // Delete deletes the key. Panics on nil key. + Delete(key []byte) + + // Iterator over a domain of keys in ascending order. End is exclusive. + // Start must be less than end, or the Iterator is invalid. + // Iterator must be closed by caller. + // To iterate over entire domain, use store.Iterator(nil, nil) + // CONTRACT: No writes may happen within a domain while an iterator exists over it. + // Exceptionally allowed for cachekv.Store, safe to write in the modules. + Iterator(start, end []byte) Iterator + + // Iterator over a domain of keys in descending order. End is exclusive. + // Start must be less than end, or the Iterator is invalid. + // Iterator must be closed by caller. + // CONTRACT: No writes may happen within a domain while an iterator exists over it. + // Exceptionally allowed for cachekv.Store, safe to write in the modules. + ReverseIterator(start, end []byte) Iterator +} + +// Alias iterator to db's Iterator for convenience. +type Iterator = dbm.Iterator + +// CacheKVStore cache-wraps a KVStore. After calling .Write() on +// the CacheKVStore, all previously created CacheKVStores on the +// object expire. +type CacheKVStore interface { + KVStore + + // Writes operations to underlying KVStore + Write() +} + +// Stores of MultiStore must implement CommitStore. +type CommitKVStore interface { + Committer + KVStore +} + +//---------------------------------------- +// CacheWrap + +// CacheWrap makes the most appropriate cache-wrap. For example, +// IAVLStore.CacheWrap() returns a CacheKVStore. CacheWrap should not return +// a Committer, since Commit cache-wraps make no sense. It can return KVStore, +// HeapStore, SpaceStore, etc. +type CacheWrap interface { + // Write syncs with the underlying store. + Write() + + // CacheWrap recursively wraps again. + CacheWrap() CacheWrap + + // CacheWrapWithTrace recursively wraps again with tracing enabled. + CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap +} + +type CacheWrapper interface { //nolint + // CacheWrap cache wraps. + CacheWrap() CacheWrap + + // CacheWrapWithTrace cache wraps with tracing enabled. + CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap +} + +//---------------------------------------- +// CommitID + +// CommitID contains the tree version number and its merkle root. +type CommitID struct { + Version int64 + Hash []byte +} + +func (cid CommitID) IsZero() bool { //nolint + return cid.Version == 0 && len(cid.Hash) == 0 +} + +func (cid CommitID) String() string { + return fmt.Sprintf("CommitID{%v:%X}", cid.Hash, cid.Version) +} + +//---------------------------------------- +// Store types + +// kind of store +type StoreType int + +const ( + //nolint + StoreTypeMulti StoreType = iota + StoreTypeDB + StoreTypeIAVL + StoreTypeTransient +) + +//---------------------------------------- +// Keys for accessing substores + +// StoreKey is a key used to index stores in a MultiStore. +type StoreKey interface { + Name() string + String() string +} + +// KVStoreKey is used for accessing substores. +// Only the pointer value should ever be used - it functions as a capabilities key. +type KVStoreKey struct { + name string +} + +// NewKVStoreKey returns a new pointer to a KVStoreKey. +// Use a pointer so keys don't collide. +func NewKVStoreKey(name string) *KVStoreKey { + return &KVStoreKey{ + name: name, + } +} + +func (key *KVStoreKey) Name() string { + return key.name +} + +func (key *KVStoreKey) String() string { + return fmt.Sprintf("KVStoreKey{%p, %s}", key, key.name) +} + +// TransientStoreKey is used for indexing transient stores in a MultiStore +type TransientStoreKey struct { + name string +} + +// Constructs new TransientStoreKey +// Must return a pointer according to the ocap principle +func NewTransientStoreKey(name string) *TransientStoreKey { + return &TransientStoreKey{ + name: name, + } +} + +// Implements StoreKey +func (key *TransientStoreKey) Name() string { + return key.name +} + +// Implements StoreKey +func (key *TransientStoreKey) String() string { + return fmt.Sprintf("TransientStoreKey{%p, %s}", key, key.name) +} + +//---------------------------------------- + +// key-value result for iterator queries +type KVPair cmn.KVPair + +//---------------------------------------- + +// TraceContext contains TraceKVStore context data. It will be written with +// every trace operation. +type TraceContext map[string]interface{} diff --git a/store/types/utils.go b/store/types/utils.go new file mode 100644 index 000000000000..a86efdb8c82f --- /dev/null +++ b/store/types/utils.go @@ -0,0 +1,98 @@ +package types + +import ( + "bytes" + + cmn "github.com/tendermint/tendermint/libs/common" +) + +// Iterator over all the keys with a certain prefix in ascending order +func KVStorePrefixIterator(kvs KVStore, prefix []byte) Iterator { + return kvs.Iterator(prefix, PrefixEndBytes(prefix)) +} + +// Iterator over all the keys with a certain prefix in descending order. +func KVStoreReversePrefixIterator(kvs KVStore, prefix []byte) Iterator { + return kvs.ReverseIterator(prefix, PrefixEndBytes(prefix)) +} + +// Compare two KVstores, return either the first key/value pair +// at which they differ and whether or not they are equal, skipping +// value comparison for a set of provided prefixes +func DiffKVStores(a KVStore, b KVStore, prefixesToSkip [][]byte) (kvA cmn.KVPair, kvB cmn.KVPair, count int64, equal bool) { + iterA := a.Iterator(nil, nil) + iterB := b.Iterator(nil, nil) + count = int64(0) + for { + if !iterA.Valid() && !iterB.Valid() { + break + } + var kvA, kvB cmn.KVPair + if iterA.Valid() { + kvA = cmn.KVPair{Key: iterA.Key(), Value: iterA.Value()} + iterA.Next() + } + if iterB.Valid() { + kvB = cmn.KVPair{Key: iterB.Key(), Value: iterB.Value()} + iterB.Next() + } + if !bytes.Equal(kvA.Key, kvB.Key) { + return kvA, kvB, count, false + } + compareValue := true + for _, prefix := range prefixesToSkip { + // Skip value comparison if we matched a prefix + if bytes.Equal(kvA.Key[:len(prefix)], prefix) { + compareValue = false + } + } + if compareValue && !bytes.Equal(kvA.Value, kvB.Value) { + return kvA, kvB, count, false + } + count++ + } + return cmn.KVPair{}, cmn.KVPair{}, count, true +} + +// PrefixEndBytes returns the []byte that would end a +// range query for all []byte with a certain prefix +// Deals with last byte of prefix being FF without overflowing +func PrefixEndBytes(prefix []byte) []byte { + if prefix == nil { + return nil + } + + end := make([]byte, len(prefix)) + copy(end, prefix) + + for { + if end[len(end)-1] != byte(255) { + end[len(end)-1]++ + break + } else { + end = end[:len(end)-1] + if len(end) == 0 { + end = nil + break + } + } + } + return end +} + +// InclusiveEndBytes returns the []byte that would end a +// range query such that the input would be included +func InclusiveEndBytes(inclusiveBytes []byte) (exclusiveBytes []byte) { + exclusiveBytes = append(inclusiveBytes, byte(0x00)) + return exclusiveBytes +} + +//---------------------------------------- +func Cp(bz []byte) (ret []byte) { + if bz == nil { + return nil + } + ret = make([]byte, len(bz)) + copy(ret, bz) + return ret +} diff --git a/store/types/validity.go b/store/types/validity.go new file mode 100644 index 000000000000..59dbd0c032bd --- /dev/null +++ b/store/types/validity.go @@ -0,0 +1,15 @@ +package types + +// Check if the key is valid(key is not nil) +func AssertValidKey(key []byte) { + if key == nil { + panic("key is nil") + } +} + +// Check if the value is valid(value is not nil) +func AssertValidValue(value []byte) { + if value == nil { + panic("value is nil") + } +} diff --git a/store/validity.go b/store/validity.go deleted file mode 100644 index 5a20f1fb3a21..000000000000 --- a/store/validity.go +++ /dev/null @@ -1,13 +0,0 @@ -package store - -func assertValidKey(key []byte) { - if key == nil { - panic("key is nil") - } -} - -func assertValidValue(value []byte) { - if value == nil { - panic("value is nil") - } -} diff --git a/types/context.go b/types/context.go index 3b815ee20319..1663a94e65ac 100644 --- a/types/context.go +++ b/types/context.go @@ -10,6 +10,9 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/store/gaskv" + stypes "github.com/cosmos/cosmos-sdk/store/types" ) /* @@ -46,7 +49,7 @@ func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, logger log.Lo c = c.WithTxBytes(nil) c = c.WithLogger(logger) c = c.WithVoteInfos(nil) - c = c.WithGasMeter(NewInfiniteGasMeter()) + c = c.WithGasMeter(stypes.NewInfiniteGasMeter()) c = c.WithMinGasPrices(DecCoins{}) c = c.WithConsensusParams(nil) return c @@ -74,12 +77,12 @@ func (c Context) Value(key interface{}) interface{} { // KVStore fetches a KVStore from the MultiStore. func (c Context) KVStore(key StoreKey) KVStore { - return c.MultiStore().GetKVStore(key).Gas(c.GasMeter(), cachedKVGasConfig) + return gaskv.NewStore(c.MultiStore().GetKVStore(key), c.GasMeter(), stypes.KVGasConfig()) } // TransientStore fetches a TransientStore from the MultiStore. func (c Context) TransientStore(key StoreKey) KVStore { - return c.MultiStore().GetKVStore(key).Gas(c.GasMeter(), cachedTransientGasConfig) + return gaskv.NewStore(c.MultiStore().GetKVStore(key), c.GasMeter(), stypes.TransientGasConfig()) } //---------------------------------------- diff --git a/types/int.go b/types/int.go index dec7e81c8278..5c4ff42450a3 100644 --- a/types/int.go +++ b/types/int.go @@ -2,7 +2,6 @@ package types import ( "encoding/json" - "math" "testing" "math/big" @@ -575,16 +574,6 @@ func UintOverflow(x Uint) bool { return x.i.Sign() == -1 || x.i.Sign() == 1 && x.i.BitLen() > 256 } -// AddUint64Overflow performs the addition operation on two uint64 integers and -// returns a boolean on whether or not the result overflows. -func AddUint64Overflow(a, b uint64) (uint64, bool) { - if math.MaxUint64-a < b { - return 0, true - } - - return a + b, false -} - // intended to be used with require/assert: require.True(IntEq(...)) func IntEq(t *testing.T, exp, got Int) (*testing.T, bool, string, string, string) { return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp.String(), got.String() diff --git a/types/int_test.go b/types/int_test.go index 18d968e903e8..a6c3c900232c 100644 --- a/types/int_test.go +++ b/types/int_test.go @@ -631,28 +631,3 @@ func TestSafeSub(t *testing.T) { ) } } - -func TestAddUint64Overflow(t *testing.T) { - testCases := []struct { - a, b uint64 - result uint64 - overflow bool - }{ - {0, 0, 0, false}, - {100, 100, 200, false}, - {math.MaxUint64 / 2, math.MaxUint64/2 + 1, math.MaxUint64, false}, - {math.MaxUint64 / 2, math.MaxUint64/2 + 2, 0, true}, - } - - for i, tc := range testCases { - res, overflow := AddUint64Overflow(tc.a, tc.b) - require.Equal( - t, tc.overflow, overflow, - "invalid overflow result; tc: #%d, a: %d, b: %d", i, tc.a, tc.b, - ) - require.Equal( - t, tc.result, res, - "invalid uint64 result; tc: #%d, a: %d, b: %d", i, tc.a, tc.b, - ) - } -} diff --git a/types/store.go b/types/store.go index 3118fba8f166..1b5b20213230 100644 --- a/types/store.go +++ b/types/store.go @@ -1,398 +1,130 @@ package types import ( - "bytes" - "fmt" - "io" - - abci "github.com/tendermint/tendermint/abci/types" cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" -) - -// NOTE: These are implemented in cosmos-sdk/store. - -// PruningStrategy specifies how old states will be deleted over time where -// keepRecent can be used with keepEvery to create a pruning "strategy". -type PruningOptions struct { - keepRecent int64 - keepEvery int64 -} - -func NewPruningOptions(keepRecent, keepEvery int64) PruningOptions { - return PruningOptions{ - keepRecent: keepRecent, - keepEvery: keepEvery, - } -} - -// How much recent state will be kept. Older state will be deleted. -func (po PruningOptions) KeepRecent() int64 { - return po.keepRecent -} - -// Keeps every N stated, deleting others. -func (po PruningOptions) KeepEvery() int64 { - return po.keepEvery -} - -type Store interface { //nolint - GetStoreType() StoreType - CacheWrapper -} - -// something that can persist to disk -type Committer interface { - Commit() CommitID - LastCommitID() CommitID - SetPruning(PruningOptions) -} - -// Stores of MultiStore must implement CommitStore. -type CommitStore interface { - Committer - Store -} - -// Queryable allows a Store to expose internal state to the abci.Query -// interface. Multistore can route requests to the proper Store. -// -// This is an optional, but useful extension to any CommitStore -type Queryable interface { - Query(abci.RequestQuery) abci.ResponseQuery -} - -//---------------------------------------- -// MultiStore -type MultiStore interface { //nolint - Store - - // Cache wrap MultiStore. - // NOTE: Caller should probably not call .Write() on each, but - // call CacheMultiStore.Write(). - CacheMultiStore() CacheMultiStore - - // Convenience for fetching substores. - // If the store does not exist, panics. - GetStore(StoreKey) Store - GetKVStore(StoreKey) KVStore - - // TracingEnabled returns if tracing is enabled for the MultiStore. - TracingEnabled() bool - - // WithTracer sets the tracer for the MultiStore that the underlying - // stores will utilize to trace operations. A MultiStore is returned. - WithTracer(w io.Writer) MultiStore - - // WithTracingContext sets the tracing context for a MultiStore. It is - // implied that the caller should update the context when necessary between - // tracing operations. A MultiStore is returned. - WithTracingContext(TraceContext) MultiStore - - // ResetTraceContext resets the current tracing context. - ResetTraceContext() MultiStore -} - -// From MultiStore.CacheMultiStore().... -type CacheMultiStore interface { - MultiStore - Write() // Writes operations to underlying KVStore -} - -// A non-cache MultiStore. -type CommitMultiStore interface { - Committer - MultiStore - - // Mount a store of type using the given db. - // If db == nil, the new store will use the CommitMultiStore db. - MountStoreWithDB(key StoreKey, typ StoreType, db dbm.DB) - - // Panics on a nil key. - GetCommitStore(key StoreKey) CommitStore - - // Panics on a nil key. - GetCommitKVStore(key StoreKey) CommitKVStore - - // Load the latest persisted version. Called once after all - // calls to Mount*Store() are complete. - LoadLatestVersion() error - - // Load a specific persisted version. When you load an old - // version, or when the last commit attempt didn't complete, - // the next commit after loading must be idempotent (return the - // same commit id). Otherwise the behavior is undefined. - LoadVersion(ver int64) error -} - -//---------subsp------------------------------- -// KVStore - -// KVStore is a simple interface to get/set data -type KVStore interface { - Store - - // Get returns nil iff key doesn't exist. Panics on nil key. - Get(key []byte) []byte - - // Has checks if a key exists. Panics on nil key. - Has(key []byte) bool - - // Set sets the key. Panics on nil key or value. - Set(key, value []byte) - - // Delete deletes the key. Panics on nil key. - Delete(key []byte) - - // Iterator over a domain of keys in ascending order. End is exclusive. - // Start must be less than end, or the Iterator is invalid. - // Iterator must be closed by caller. - // To iterate over entire domain, use store.Iterator(nil, nil) - // CONTRACT: No writes may happen within a domain while an iterator exists over it. - Iterator(start, end []byte) Iterator - - // Iterator over a domain of keys in descending order. End is exclusive. - // Start must be less than end, or the Iterator is invalid. - // Iterator must be closed by caller. - // CONTRACT: No writes may happen within a domain while an iterator exists over it. - ReverseIterator(start, end []byte) Iterator - - // TODO Not yet implemented. - // CreateSubKVStore(key *storeKey) (KVStore, error) - - // TODO Not yet implemented. - // GetSubKVStore(key *storeKey) KVStore - - // Prefix applied keys with the argument - // CONTRACT: when Prefix is called on a KVStore more than once, - // the concatanation of the prefixes is applied - Prefix(prefix []byte) KVStore + "github.com/cosmos/cosmos-sdk/store/types" +) - // Gas consuming store - // CONTRACT: when Gas is called on a KVStore more than once, - // the concatanation of the meters/configs is applied - Gas(GasMeter, GasConfig) KVStore -} +// nolint - reexport +type ( + PruningOptions = types.PruningOptions +) -// Alias iterator to db's Iterator for convenience. -type Iterator = dbm.Iterator +// nolint - reexport +type ( + Store = types.Store + Committer = types.Committer + CommitStore = types.CommitStore + Queryable = types.Queryable + MultiStore = types.MultiStore + CacheMultiStore = types.CacheMultiStore + CommitMultiStore = types.CommitMultiStore + KVStore = types.KVStore + Iterator = types.Iterator +) // Iterator over all the keys with a certain prefix in ascending order func KVStorePrefixIterator(kvs KVStore, prefix []byte) Iterator { - return kvs.Iterator(prefix, PrefixEndBytes(prefix)) + return types.KVStorePrefixIterator(kvs, prefix) } // Iterator over all the keys with a certain prefix in descending order. func KVStoreReversePrefixIterator(kvs KVStore, prefix []byte) Iterator { - return kvs.ReverseIterator(prefix, PrefixEndBytes(prefix)) + return types.KVStoreReversePrefixIterator(kvs, prefix) } // Compare two KVstores, return either the first key/value pair // at which they differ and whether or not they are equal, skipping // value comparison for a set of provided prefixes func DiffKVStores(a KVStore, b KVStore, prefixesToSkip [][]byte) (kvA cmn.KVPair, kvB cmn.KVPair, count int64, equal bool) { - iterA := a.Iterator(nil, nil) - iterB := b.Iterator(nil, nil) - count = int64(0) - for { - if !iterA.Valid() && !iterB.Valid() { - break - } - var kvA, kvB cmn.KVPair - if iterA.Valid() { - kvA = cmn.KVPair{Key: iterA.Key(), Value: iterA.Value()} - iterA.Next() - } - if iterB.Valid() { - kvB = cmn.KVPair{Key: iterB.Key(), Value: iterB.Value()} - iterB.Next() - } - if !bytes.Equal(kvA.Key, kvB.Key) { - return kvA, kvB, count, false - } - compareValue := true - for _, prefix := range prefixesToSkip { - // Skip value comparison if we matched a prefix - if bytes.Equal(kvA.Key[:len(prefix)], prefix) { - compareValue = false - } - } - if compareValue && !bytes.Equal(kvA.Value, kvB.Value) { - return kvA, kvB, count, false - } - count++ - } - return cmn.KVPair{}, cmn.KVPair{}, count, true -} - -// CacheKVStore cache-wraps a KVStore. After calling .Write() on -// the CacheKVStore, all previously created CacheKVStores on the -// object expire. -type CacheKVStore interface { - KVStore - - // Writes operations to underlying KVStore - Write() + return types.DiffKVStores(a, b, prefixesToSkip) } -// Stores of MultiStore must implement CommitStore. -type CommitKVStore interface { - Committer - KVStore -} - -//---------------------------------------- -// CacheWrap - -// CacheWrap makes the most appropriate cache-wrap. For example, -// IAVLStore.CacheWrap() returns a CacheKVStore. CacheWrap should not return -// a Committer, since Commit cache-wraps make no sense. It can return KVStore, -// HeapStore, SpaceStore, etc. -type CacheWrap interface { - // Write syncs with the underlying store. - Write() - - // CacheWrap recursively wraps again. - CacheWrap() CacheWrap - - // CacheWrapWithTrace recursively wraps again with tracing enabled. - CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap -} - -type CacheWrapper interface { //nolint - // CacheWrap cache wraps. - CacheWrap() CacheWrap - - // CacheWrapWithTrace cache wraps with tracing enabled. - CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap -} - -//---------------------------------------- -// CommitID - -// CommitID contains the tree version number and its merkle root. -type CommitID struct { - Version int64 - Hash []byte -} - -func (cid CommitID) IsZero() bool { //nolint - return cid.Version == 0 && len(cid.Hash) == 0 -} - -func (cid CommitID) String() string { - return fmt.Sprintf("CommitID{%v:%X}", cid.Hash, cid.Version) -} - -//---------------------------------------- -// Store types +// nolint - reexport +type ( + CacheKVStore = types.CacheKVStore + CommitKVStore = types.CommitKVStore + CacheWrap = types.CacheWrap + CacheWrapper = types.CacheWrapper + CommitID = types.CommitID +) -// kind of store -type StoreType int +// nolint - reexport +type StoreType = types.StoreType +// nolint - reexport const ( - //nolint - StoreTypeMulti StoreType = iota - StoreTypeDB - StoreTypeIAVL - StoreTypeTransient + StoreTypeMulti = types.StoreTypeMulti + StoreTypeDB = types.StoreTypeDB + StoreTypeIAVL = types.StoreTypeIAVL + StoreTypeTransient = types.StoreTypeTransient ) -//---------------------------------------- -// Keys for accessing substores - -// StoreKey is a key used to index stores in a MultiStore. -type StoreKey interface { - Name() string - String() string -} - -// KVStoreKey is used for accessing substores. -// Only the pointer value should ever be used - it functions as a capabilities key. -type KVStoreKey struct { - name string -} +// nolint - reexport +type ( + StoreKey = types.StoreKey + KVStoreKey = types.KVStoreKey + TransientStoreKey = types.TransientStoreKey +) // NewKVStoreKey returns a new pointer to a KVStoreKey. // Use a pointer so keys don't collide. func NewKVStoreKey(name string) *KVStoreKey { - return &KVStoreKey{ - name: name, - } + return types.NewKVStoreKey(name) } -func (key *KVStoreKey) Name() string { - return key.name -} - -func (key *KVStoreKey) String() string { - return fmt.Sprintf("KVStoreKey{%p, %s}", key, key.name) +// Constructs new TransientStoreKey +// Must return a pointer according to the ocap principle +func NewTransientStoreKey(name string) *TransientStoreKey { + return types.NewTransientStoreKey(name) } // PrefixEndBytes returns the []byte that would end a // range query for all []byte with a certain prefix // Deals with last byte of prefix being FF without overflowing func PrefixEndBytes(prefix []byte) []byte { - if prefix == nil { - return nil - } - - end := make([]byte, len(prefix)) - copy(end, prefix) - - for { - if end[len(end)-1] != byte(255) { - end[len(end)-1]++ - break - } else { - end = end[:len(end)-1] - if len(end) == 0 { - end = nil - break - } - } - } - return end + return types.PrefixEndBytes(prefix) } // InclusiveEndBytes returns the []byte that would end a // range query such that the input would be included func InclusiveEndBytes(inclusiveBytes []byte) (exclusiveBytes []byte) { - exclusiveBytes = append(inclusiveBytes, byte(0x00)) - return exclusiveBytes -} - -// TransientStoreKey is used for indexing transient stores in a MultiStore -type TransientStoreKey struct { - name string -} - -// Constructs new TransientStoreKey -// Must return a pointer according to the ocap principle -func NewTransientStoreKey(name string) *TransientStoreKey { - return &TransientStoreKey{ - name: name, - } -} - -// Implements StoreKey -func (key *TransientStoreKey) Name() string { - return key.name -} - -// Implements StoreKey -func (key *TransientStoreKey) String() string { - return fmt.Sprintf("TransientStoreKey{%p, %s}", key, key.name) + return types.InclusiveEndBytes(inclusiveBytes) } //---------------------------------------- // key-value result for iterator queries -type KVPair cmn.KVPair +type KVPair = types.KVPair //---------------------------------------- // TraceContext contains TraceKVStore context data. It will be written with // every trace operation. -type TraceContext map[string]interface{} +type TraceContext = types.TraceContext + +// -------------------------------------- + +// nolint - reexport +type ( + Gas = types.Gas + GasMeter = types.GasMeter + GasConfig = types.GasConfig +) + +// nolint - reexport +func NewGasMeter(limit Gas) GasMeter { + return types.NewGasMeter(limit) +} + +// nolint - reexport +type ( + ErrorOutOfGas = types.ErrorOutOfGas + ErrorGasOverflow = types.ErrorGasOverflow +) + +// nolint - reexport +func NewInfiniteGasMeter() GasMeter { + return types.NewInfiniteGasMeter() +} diff --git a/x/params/keeper_test.go b/x/params/keeper_test.go index 585db3c410e5..cda57de8e887 100644 --- a/x/params/keeper_test.go +++ b/x/params/keeper_test.go @@ -12,6 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" + "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -71,7 +72,7 @@ func TestKeeper(t *testing.T) { ctx := defaultContext(skey, tkey) keeper := NewKeeper(cdc, skey, tkey) space := keeper.Subspace("test").WithTypeTable(table) - store := ctx.KVStore(skey).Prefix([]byte("test/")) + store := prefix.NewStore(ctx.KVStore(skey), []byte("test/")) // Set params for i, kv := range kvs { @@ -177,7 +178,7 @@ func TestSubspace(t *testing.T) { []byte("struct"), s{}, ) - store := ctx.KVStore(key).Prefix([]byte("test/")) + store := prefix.NewStore(ctx.KVStore(key), []byte("test/")) space := keeper.Subspace("test").WithTypeTable(table) // Test space.Set, space.Modified diff --git a/x/params/subspace/subspace.go b/x/params/subspace/subspace.go index 2ace1923276d..cf8e345dd8fe 100644 --- a/x/params/subspace/subspace.go +++ b/x/params/subspace/subspace.go @@ -5,6 +5,8 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/cosmos-sdk/store/prefix" ) const ( @@ -69,14 +71,14 @@ func (s Subspace) WithTypeTable(table TypeTable) Subspace { func (s Subspace) kvStore(ctx sdk.Context) sdk.KVStore { // append here is safe, appends within a function won't cause // weird side effects when its singlethreaded - return ctx.KVStore(s.key).Prefix(append(s.name, '/')) + return prefix.NewStore(ctx.KVStore(s.key), append(s.name, '/')) } // Returns a KVStore identical with ctx.TransientStore(s.tkey).Prefix() func (s Subspace) transientStore(ctx sdk.Context) sdk.KVStore { // append here is safe, appends within a function won't cause // weird side effects when its singlethreaded - return ctx.TransientStore(s.tkey).Prefix(append(s.name, '/')) + return prefix.NewStore(ctx.TransientStore(s.tkey), append(s.name, '/')) } // Get parameter from store diff --git a/x/params/subspace/test_common.go b/x/params/subspace/test_common.go index e39b9c1b2ddf..f6bf9c73f1ca 100644 --- a/x/params/subspace/test_common.go +++ b/x/params/subspace/test_common.go @@ -27,8 +27,8 @@ func DefaultTestComponents(t *testing.T, table TypeTable) (sdk.Context, Subspace tkey := sdk.NewTransientStoreKey(TStoreKey) db := dbm.NewMemDB() ms := store.NewCommitMultiStore(db) - ms.WithTracer(os.Stdout) - ms.WithTracingContext(sdk.TraceContext{}) + ms.SetTracer(os.Stdout) + ms.SetTracingContext(sdk.TraceContext{}) ms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) ms.MountStoreWithDB(tkey, sdk.StoreTypeTransient, db) err := ms.LoadLatestVersion()