Store Refactor 1 (#2985)
This commit is contained in:
parent
45d59b0792
commit
08e62fb157
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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)),
|
||||
)
|
||||
}
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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
130
store/README.md
Normal 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.
|
||||
@ -1,4 +1,4 @@
|
||||
package store
|
||||
package cachekv
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -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
196
store/cachekv/store.go
Normal 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,
|
||||
}
|
||||
}
|
||||
@ -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))
|
||||
@ -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
135
store/cachemulti/store.go
Normal 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)
|
||||
}
|
||||
@ -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
34
store/dbadapter/store.go
Normal 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{}
|
||||
@ -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) {}
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
78
store/gaskv/store_test.go
Normal 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")
|
||||
}
|
||||
@ -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)
|
||||
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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
7
store/iavl/wire.go
Normal file
@ -0,0 +1,7 @@
|
||||
package iavl
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
)
|
||||
|
||||
var cdc = codec.New()
|
||||
@ -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()
|
||||
@ -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) {
|
||||
@ -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()
|
||||
@ -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)
|
||||
@ -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
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
*/
|
||||
@ -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())
|
||||
}
|
||||
@ -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
|
||||
)
|
||||
33
store/rootmulti/dbadapter.go
Normal file
33
store/rootmulti/dbadapter.go
Normal 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) {}
|
||||
@ -1,4 +1,4 @@
|
||||
package store
|
||||
package rootmulti
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
@ -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
7
store/rootmulti/wire.go
Normal file
@ -0,0 +1,7 @@
|
||||
package rootmulti
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
)
|
||||
|
||||
var cdc = codec.New()
|
||||
26
store/store.go
Normal file
26
store/store.go
Normal 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
|
||||
}
|
||||
@ -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),
|
||||
@ -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
42
store/transient/store.go
Normal 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
|
||||
}
|
||||
@ -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))
|
||||
|
||||
@ -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
|
||||
}
|
||||
@ -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})
|
||||
}
|
||||
@ -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
35
store/types/pruning.go
Normal 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
274
store/types/store.go
Normal 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
98
store/types/utils.go
Normal 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
15
store/types/validity.go
Normal 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")
|
||||
}
|
||||
}
|
||||
@ -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")
|
||||
}
|
||||
}
|
||||
@ -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())
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
11
types/int.go
11
types/int.go
@ -2,7 +2,6 @@ package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"math/big"
|
||||
@ -575,16 +574,6 @@ func UintOverflow(x Uint) bool {
|
||||
return x.i.Sign() == -1 || x.i.Sign() == 1 && x.i.BitLen() > 256
|
||||
}
|
||||
|
||||
// AddUint64Overflow performs the addition operation on two uint64 integers and
|
||||
// returns a boolean on whether or not the result overflows.
|
||||
func AddUint64Overflow(a, b uint64) (uint64, bool) {
|
||||
if math.MaxUint64-a < b {
|
||||
return 0, true
|
||||
}
|
||||
|
||||
return a + b, false
|
||||
}
|
||||
|
||||
// intended to be used with require/assert: require.True(IntEq(...))
|
||||
func IntEq(t *testing.T, exp, got Int) (*testing.T, bool, string, string, string) {
|
||||
return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp.String(), got.String()
|
||||
|
||||
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
422
types/store.go
422
types/store.go
@ -1,398 +1,130 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
|
||||
"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()
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user