Store Refactor 1 (#2985)

This commit is contained in:
Joon 2019-02-01 17:03:09 -08:00 committed by Jack Zampolin
parent 45d59b0792
commit 08e62fb157
58 changed files with 1758 additions and 1611 deletions

View File

@ -53,6 +53,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

View File

@ -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 {

View File

@ -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"

View File

@ -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)
@ -254,7 +253,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
}

View File

@ -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)),
)
}

View File

@ -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()

View File

@ -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")
}

130
store/README.md Normal file
View File

@ -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.

View File

@ -1,4 +1,4 @@
package store
package cachekv
import (
"bytes"

View File

@ -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,

196
store/cachekv/store.go Normal file
View File

@ -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,
}
}

View File

@ -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))

View File

@ -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,
}
}

135
store/cachemulti/store.go Normal file
View File

@ -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)
}

View File

@ -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))
}

34
store/dbadapter/store.go Normal file
View File

@ -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{}

View File

@ -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) {}

View File

@ -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
}

View File

@ -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)
}

78
store/gaskv/store_test.go Normal file
View File

@ -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")
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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})
}

7
store/iavl/wire.go Normal file
View File

@ -0,0 +1,7 @@
package iavl
import (
"github.com/cosmos/cosmos-sdk/codec"
)
var cdc = codec.New()

View File

@ -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()

View File

@ -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) {

View File

@ -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()

View File

@ -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)

View File

@ -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
}

View File

@ -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)
}
*/

View File

@ -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())
}

View File

@ -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
)

View File

@ -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) {}

View File

@ -1,4 +1,4 @@
package store
package rootmulti
import (
"bytes"

View File

@ -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.

View File

@ -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/<version>
)
// 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 `/<substore>/<path>`, and trimmed to `/<path>` 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 /<storeName>[/<subpath>]
// 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

View File

@ -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()

7
store/rootmulti/wire.go Normal file
View File

@ -0,0 +1,7 @@
package rootmulti
import (
"github.com/cosmos/cosmos-sdk/codec"
)
var cdc = codec.New()

26
store/store.go Normal file
View File

@ -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
}

View File

@ -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),

View File

@ -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())
}

42
store/transient/store.go Normal file
View File

@ -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
}

View File

@ -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))

View File

@ -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
}

View File

@ -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})
}

View File

@ -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,
)
}
}

35
store/types/pruning.go Normal file
View File

@ -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)
)

274
store/types/store.go Normal file
View File

@ -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{}

98
store/types/utils.go Normal file
View File

@ -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
}

15
store/types/validity.go Normal file
View File

@ -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")
}
}

View File

@ -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")
}
}

View File

@ -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())
}
//----------------------------------------

View File

@ -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()

View File

@ -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,
)
}
}

View File

@ -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"
"github.com/cosmos/cosmos-sdk/store/types"
)
// NOTE: These are implemented in cosmos-sdk/store.
// nolint - reexport
type (
PruningOptions = types.PruningOptions
)
// 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
// 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
}
// 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
return types.DiffKVStores(a, b, prefixesToSkip)
}
// 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
// nolint - reexport
type (
CacheKVStore = types.CacheKVStore
CommitKVStore = types.CommitKVStore
CacheWrap = types.CacheWrap
CacheWrapper = types.CacheWrapper
CommitID = types.CommitID
)
//----------------------------------------
// Keys for accessing substores
// nolint - reexport
type StoreType = types.StoreType
// StoreKey is a key used to index stores in a MultiStore.
type StoreKey interface {
Name() string
String() string
}
// nolint - reexport
const (
StoreTypeMulti = types.StoreTypeMulti
StoreTypeDB = types.StoreTypeDB
StoreTypeIAVL = types.StoreTypeIAVL
StoreTypeTransient = types.StoreTypeTransient
)
// 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()
}

View File

@ -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

View File

@ -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

View File

@ -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()