Skip to content

Commit

Permalink
Merge pull request #1481: Transient Stores
Browse files Browse the repository at this point in the history
move gasconfig to types, make GetKVStoreWithGas take GasConfig

fix lint

modify transientstore

in progress

add test for transientstore

fix errors

fix test

fix errors and lint

last fix

in progress

move transient to KVStore

fix syntax errors

finalize rebase

remove NewMemDBStoreAdapter for lint

apply requests

apply requests

apply requests

add CHANGELOG

add tests

fix getter

in progress

finalize rebase

fix lint

partially apply requests

assert -> require

apply requests

fix test

Changelog => Pending

add TransientStoreKey

fix lint

fix lint
  • Loading branch information
mossid committed Jul 23, 2018
1 parent 63f70cb commit f27d375
Show file tree
Hide file tree
Showing 21 changed files with 347 additions and 89 deletions.
3 changes: 3 additions & 0 deletions PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ FEATURES
* [baseapp] Initialize validator set on ResponseInitChain
* [cosmos-sdk-cli] Added support for cosmos-sdk-cli tool under cosmos-sdk/cmd
* This allows SDK users to initialize a new project repository.
* Added support for cosmos-sdk-cli tool under cosmos-sdk/cmd
* This allows SDK users to init a new project repository with a single command.
* [store] Add transient store

IMPROVEMENTS
* [baseapp] Allow any alphanumeric character in route
Expand Down
Binary file added cmd/gaia/cmd/gaiadebug/gaiadebug
Binary file not shown.
12 changes: 8 additions & 4 deletions server/mock/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)

var _ sdk.MultiStore = multiStore{}

type multiStore struct {
kv map[sdk.StoreKey]kvStore
}
Expand Down Expand Up @@ -76,10 +78,6 @@ func (ms multiStore) GetKVStore(key sdk.StoreKey) sdk.KVStore {
return ms.kv[key]
}

func (ms multiStore) GetKVStoreWithGas(meter sdk.GasMeter, key sdk.StoreKey) sdk.KVStore {
panic("not implemented")
}

func (ms multiStore) GetStore(key sdk.StoreKey) sdk.Store {
panic("not implemented")
}
Expand All @@ -88,6 +86,8 @@ func (ms multiStore) GetStoreType() sdk.StoreType {
panic("not implemented")
}

var _ sdk.KVStore = kvStore{}

type kvStore struct {
store map[string][]byte
}
Expand Down Expand Up @@ -129,6 +129,10 @@ func (kv kvStore) Prefix(prefix []byte) sdk.KVStore {
panic("not implemented")
}

func (kv kvStore) Gas(meter sdk.GasMeter, config sdk.GasConfig) sdk.KVStore {
panic("not implmeneted")
}

func (kv kvStore) Iterator(start, end []byte) sdk.Iterator {
panic("not implemented")
}
Expand Down
5 changes: 5 additions & 0 deletions store/cachekvstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ 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()
Expand Down
4 changes: 2 additions & 2 deletions store/cachemultistore.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,6 @@ func (cms cacheMultiStore) GetKVStore(key StoreKey) KVStore {
}

// Implements MultiStore.
func (cms cacheMultiStore) GetKVStoreWithGas(meter sdk.GasMeter, key StoreKey) KVStore {
return NewGasKVStore(meter, cms.GetKVStore(key))
func (cms cacheMultiStore) GetKVStoreWithGas(meter sdk.GasMeter, config sdk.GasConfig, key StoreKey) KVStore {
return NewGasKVStore(meter, config, cms.GetKVStore(key))
}
8 changes: 7 additions & 1 deletion store/dbstoreadapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
dbm "github.com/tendermint/tendermint/libs/db"
)

// Wrapper type for dbm.Db with implementation of KVStore
type dbStoreAdapter struct {
dbm.DB
}
Expand All @@ -31,5 +32,10 @@ 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{dbm.DB(nil)}
var _ KVStore = dbStoreAdapter{}
67 changes: 36 additions & 31 deletions store/gaskvstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,21 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
)

// nolint
const (
HasCost = 10
ReadCostFlat = 10
ReadCostPerByte = 1
WriteCostFlat = 10
WriteCostPerByte = 10
KeyCostFlat = 5
ValueCostFlat = 10
ValueCostPerByte = 1
)
var _ KVStore = &gasKVStore{}

// gasKVStore applies gas tracking to an underlying kvstore
type gasKVStore struct {
gasMeter sdk.GasMeter
parent sdk.KVStore
gasMeter sdk.GasMeter
gasConfig sdk.GasConfig
parent sdk.KVStore
}

// nolint
func NewGasKVStore(gasMeter sdk.GasMeter, parent sdk.KVStore) *gasKVStore {
func NewGasKVStore(gasMeter sdk.GasMeter, gasConfig sdk.GasConfig, parent sdk.KVStore) *gasKVStore {
kvs := &gasKVStore{
gasMeter: gasMeter,
parent: parent,
gasMeter: gasMeter,
gasConfig: gasConfig,
parent: parent,
}
return kvs
}
Expand All @@ -40,24 +32,25 @@ func (gi *gasKVStore) GetStoreType() sdk.StoreType {

// Implements KVStore.
func (gi *gasKVStore) Get(key []byte) (value []byte) {
gi.gasMeter.ConsumeGas(ReadCostFlat, "GetFlat")
gi.gasMeter.ConsumeGas(gi.gasConfig.ReadCostFlat, "ReadFlat")
value = gi.parent.Get(key)
// TODO overflow-safe math?
gi.gasMeter.ConsumeGas(ReadCostPerByte*sdk.Gas(len(value)), "ReadPerByte")
gi.gasMeter.ConsumeGas(gi.gasConfig.ReadCostPerByte*sdk.Gas(len(value)), "ReadPerByte")

return value
}

// Implements KVStore.
func (gi *gasKVStore) Set(key []byte, value []byte) {
gi.gasMeter.ConsumeGas(WriteCostFlat, "SetFlat")
gi.gasMeter.ConsumeGas(gi.gasConfig.WriteCostFlat, "WriteFlat")
// TODO overflow-safe math?
gi.gasMeter.ConsumeGas(WriteCostPerByte*sdk.Gas(len(value)), "SetPerByte")
gi.gasMeter.ConsumeGas(gi.gasConfig.WriteCostPerByte*sdk.Gas(len(value)), "WritePerByte")
gi.parent.Set(key, value)
}

// Implements KVStore.
func (gi *gasKVStore) Has(key []byte) bool {
gi.gasMeter.ConsumeGas(HasCost, "Has")
gi.gasMeter.ConsumeGas(gi.gasConfig.HasCost, "Has")
return gi.parent.Has(key)
}

Expand All @@ -69,7 +62,17 @@ func (gi *gasKVStore) Delete(key []byte) {

// Implements KVStore
func (gi *gasKVStore) Prefix(prefix []byte) KVStore {
return prefixStore{gi, prefix}
// Keep gasstore layer at the top
return &gasKVStore{
gasMeter: gi.gasMeter,
gasConfig: gi.gasConfig,
parent: prefixStore{gi.parent, prefix},
}
}

// Implements KVStore
func (gi *gasKVStore) Gas(meter GasMeter, config GasConfig) KVStore {
return NewGasKVStore(meter, config, gi)
}

// Implements KVStore.
Expand Down Expand Up @@ -99,18 +102,20 @@ func (gi *gasKVStore) iterator(start, end []byte, ascending bool) sdk.Iterator {
} else {
parent = gi.parent.ReverseIterator(start, end)
}
return newGasIterator(gi.gasMeter, parent)
return newGasIterator(gi.gasMeter, gi.gasConfig, parent)
}

type gasIterator struct {
gasMeter sdk.GasMeter
parent sdk.Iterator
gasMeter sdk.GasMeter
gasConfig sdk.GasConfig
parent sdk.Iterator
}

func newGasIterator(gasMeter sdk.GasMeter, parent sdk.Iterator) sdk.Iterator {
func newGasIterator(gasMeter sdk.GasMeter, gasConfig sdk.GasConfig, parent sdk.Iterator) sdk.Iterator {
return &gasIterator{
gasMeter: gasMeter,
parent: parent,
gasMeter: gasMeter,
gasConfig: gasConfig,
parent: parent,
}
}

Expand All @@ -131,16 +136,16 @@ func (g *gasIterator) Next() {

// Implements Iterator.
func (g *gasIterator) Key() (key []byte) {
g.gasMeter.ConsumeGas(KeyCostFlat, "KeyFlat")
g.gasMeter.ConsumeGas(g.gasConfig.KeyCostFlat, "KeyFlat")
key = g.parent.Key()
return key
}

// Implements Iterator.
func (g *gasIterator) Value() (value []byte) {
value = g.parent.Value()
g.gasMeter.ConsumeGas(ValueCostFlat, "ValueFlat")
g.gasMeter.ConsumeGas(ValueCostPerByte*sdk.Gas(len(value)), "ValuePerByte")
g.gasMeter.ConsumeGas(g.gasConfig.ValueCostFlat, "ValueFlat")
g.gasMeter.ConsumeGas(g.gasConfig.ValueCostPerByte*sdk.Gas(len(value)), "ValuePerByte")
return value
}

Expand Down
49 changes: 43 additions & 6 deletions store/gaskvstore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,23 @@ package store
import (
"testing"

dbm "github.com/tendermint/tendermint/libs/db"

sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/stretchr/testify/require"
dbm "github.com/tendermint/tendermint/libs/db"
)

func newGasKVStore() KVStore {
meter := sdk.NewGasMeter(1000)
mem := dbStoreAdapter{dbm.NewMemDB()}
return NewGasKVStore(meter, mem)
return NewGasKVStore(meter, sdk.DefaultGasConfig(), mem)
}

func TestGasKVStoreBasic(t *testing.T) {
mem := dbStoreAdapter{dbm.NewMemDB()}
meter := sdk.NewGasMeter(1000)
st := NewGasKVStore(meter, mem)
st := NewGasKVStore(meter, sdk.DefaultGasConfig(), 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)))
Expand All @@ -29,7 +31,7 @@ func TestGasKVStoreBasic(t *testing.T) {
func TestGasKVStoreIterator(t *testing.T) {
mem := dbStoreAdapter{dbm.NewMemDB()}
meter := sdk.NewGasMeter(1000)
st := NewGasKVStore(meter, mem)
st := NewGasKVStore(meter, sdk.DefaultGasConfig(), 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))
Expand All @@ -53,16 +55,51 @@ func TestGasKVStoreIterator(t *testing.T) {
func TestGasKVStoreOutOfGasSet(t *testing.T) {
mem := dbStoreAdapter{dbm.NewMemDB()}
meter := sdk.NewGasMeter(0)
st := NewGasKVStore(meter, mem)
st := NewGasKVStore(meter, sdk.DefaultGasConfig(), 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(200)
st := NewGasKVStore(meter, mem)
st := NewGasKVStore(meter, sdk.DefaultGasConfig(), 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(10000)

store = store.Gas(meter, sdk.GasConfig{HasCost: 10})
require.Equal(t, int64(0), meter.GasConsumed())

store.Has([]byte("key"))
require.Equal(t, int64(10), meter.GasConsumed())

store = store.Gas(meter, sdk.GasConfig{HasCost: 20})

store.Has([]byte("key"))
require.Equal(t, int64(40), meter.GasConsumed())
}

func TestGasKVStoreWrap(t *testing.T) {
db := dbm.NewMemDB()
tree, _ := newTree(t, db)
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)

}
6 changes: 5 additions & 1 deletion store/iavlstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ func newIAVLStore(tree *iavl.VersionedTree, numRecent int64, storeEvery int64) *

// Implements Committer.
func (st *iavlStore) Commit() CommitID {

// Save a new version.
hash, version, err := st.tree.SaveVersion()
if err != nil {
Expand Down Expand Up @@ -161,6 +160,11 @@ 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 {
return newIAVLIterator(st.tree.Tree(), start, end, true)
Expand Down
1 change: 1 addition & 0 deletions store/iavlstore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@ func TestIAVLStoreQuery(t *testing.T) {
require.Equal(t, uint32(sdk.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, v2, qres.Value)
Expand Down
Loading

0 comments on commit f27d375

Please sign in to comment.