diff --git a/mock/store.go b/mock/store.go index 329eb250bb..7f62234eaf 100644 --- a/mock/store.go +++ b/mock/store.go @@ -50,6 +50,10 @@ 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") } diff --git a/store/cachemultistore.go b/store/cachemultistore.go index b1a7548811..47878fb155 100644 --- a/store/cachemultistore.go +++ b/store/cachemultistore.go @@ -72,3 +72,8 @@ func (cms cacheMultiStore) GetStore(key StoreKey) Store { func (cms cacheMultiStore) GetKVStore(key StoreKey) KVStore { return cms.stores[key].(KVStore) } + +// Implements MultiStore. +func (cms cacheMultiStore) GetKVStoreWithGas(meter sdk.GasMeter, key StoreKey) KVStore { + return NewGasKVStore(meter, cms.GetKVStore(key)) +} diff --git a/types/gaskvstore.go b/store/gaskvstore.go similarity index 68% rename from types/gaskvstore.go rename to store/gaskvstore.go index ed8fc0ea9a..9f50f34441 100644 --- a/types/gaskvstore.go +++ b/store/gaskvstore.go @@ -1,4 +1,8 @@ -package types +package store + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) // nolint const ( @@ -14,12 +18,12 @@ const ( // gasKVStore applies gas tracking to an underlying kvstore type gasKVStore struct { - gasMeter GasMeter - parent KVStore + gasMeter sdk.GasMeter + parent sdk.KVStore } // nolint -func NewGasKVStore(gasMeter GasMeter, parent KVStore) *gasKVStore { +func NewGasKVStore(gasMeter sdk.GasMeter, parent sdk.KVStore) *gasKVStore { kvs := &gasKVStore{ gasMeter: gasMeter, parent: parent, @@ -28,7 +32,7 @@ func NewGasKVStore(gasMeter GasMeter, parent KVStore) *gasKVStore { } // Implements Store. -func (gi *gasKVStore) GetStoreType() StoreType { +func (gi *gasKVStore) GetStoreType() sdk.StoreType { return gi.parent.GetStoreType() } @@ -37,7 +41,7 @@ func (gi *gasKVStore) Get(key []byte) (value []byte) { gi.gasMeter.ConsumeGas(ReadCostFlat, "GetFlat") value = gi.parent.Get(key) // TODO overflow-safe math? - gi.gasMeter.ConsumeGas(ReadCostPerByte*Gas(len(value)), "ReadPerByte") + gi.gasMeter.ConsumeGas(ReadCostPerByte*sdk.Gas(len(value)), "ReadPerByte") return value } @@ -45,7 +49,7 @@ func (gi *gasKVStore) Get(key []byte) (value []byte) { func (gi *gasKVStore) Set(key []byte, value []byte) { gi.gasMeter.ConsumeGas(WriteCostFlat, "SetFlat") // TODO overflow-safe math? - gi.gasMeter.ConsumeGas(WriteCostPerByte*Gas(len(value)), "SetPerByte") + gi.gasMeter.ConsumeGas(WriteCostPerByte*sdk.Gas(len(value)), "SetPerByte") gi.parent.Set(key, value) } @@ -62,32 +66,32 @@ func (gi *gasKVStore) Delete(key []byte) { } // Implements KVStore. -func (gi *gasKVStore) Iterator(start, end []byte) Iterator { +func (gi *gasKVStore) Iterator(start, end []byte) sdk.Iterator { return gi.iterator(start, end, true) } // Implements KVStore. -func (gi *gasKVStore) ReverseIterator(start, end []byte) Iterator { +func (gi *gasKVStore) ReverseIterator(start, end []byte) sdk.Iterator { return gi.iterator(start, end, false) } // Implements KVStore. -func (gi *gasKVStore) SubspaceIterator(prefix []byte) Iterator { - return gi.iterator(prefix, PrefixEndBytes(prefix), true) +func (gi *gasKVStore) SubspaceIterator(prefix []byte) sdk.Iterator { + return gi.iterator(prefix, sdk.PrefixEndBytes(prefix), true) } // Implements KVStore. -func (gi *gasKVStore) ReverseSubspaceIterator(prefix []byte) Iterator { - return gi.iterator(prefix, PrefixEndBytes(prefix), false) +func (gi *gasKVStore) ReverseSubspaceIterator(prefix []byte) sdk.Iterator { + return gi.iterator(prefix, sdk.PrefixEndBytes(prefix), false) } // Implements KVStore. -func (gi *gasKVStore) CacheWrap() CacheWrap { +func (gi *gasKVStore) CacheWrap() sdk.CacheWrap { panic("you cannot CacheWrap a GasKVStore") } -func (gi *gasKVStore) iterator(start, end []byte, ascending bool) Iterator { - var parent Iterator +func (gi *gasKVStore) iterator(start, end []byte, ascending bool) sdk.Iterator { + var parent sdk.Iterator if ascending { parent = gi.parent.Iterator(start, end) } else { @@ -97,11 +101,11 @@ func (gi *gasKVStore) iterator(start, end []byte, ascending bool) Iterator { } type gasIterator struct { - gasMeter GasMeter - parent Iterator + gasMeter sdk.GasMeter + parent sdk.Iterator } -func newGasIterator(gasMeter GasMeter, parent Iterator) Iterator { +func newGasIterator(gasMeter sdk.GasMeter, parent sdk.Iterator) sdk.Iterator { return &gasIterator{ gasMeter: gasMeter, parent: parent, @@ -134,7 +138,7 @@ func (g *gasIterator) Key() (key []byte) { func (g *gasIterator) Value() (value []byte) { value = g.parent.Value() g.gasMeter.ConsumeGas(ValueCostFlat, "ValueFlat") - g.gasMeter.ConsumeGas(ValueCostPerByte*Gas(len(value)), "ValuePerByte") + g.gasMeter.ConsumeGas(ValueCostPerByte*sdk.Gas(len(value)), "ValuePerByte") return value } diff --git a/store/gaskvstore_test.go b/store/gaskvstore_test.go new file mode 100644 index 0000000000..4879c327c2 --- /dev/null +++ b/store/gaskvstore_test.go @@ -0,0 +1,34 @@ +package store + +import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + dbm "github.com/tendermint/tmlibs/db" +) + +func newGasKVStore() KVStore { + meter := sdk.NewGasMeter(1000) + mem := dbStoreAdapter{dbm.NewMemDB()} + return NewGasKVStore(meter, mem) +} + +func TestGasKVStoreBasic(t *testing.T) { + mem := dbStoreAdapter{dbm.NewMemDB()} + meter := sdk.NewGasMeter(1000) + st := NewGasKVStore(meter, mem) + + require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") + + mem.Set(keyFmt(1), valFmt(1)) + st.Set(keyFmt(1), valFmt(1)) + require.Equal(t, valFmt(1), st.Get(keyFmt(1))) +} + +func TestGasKVStoreOutOfGas(t *testing.T) { + mem := dbStoreAdapter{dbm.NewMemDB()} + meter := sdk.NewGasMeter(0) + st := NewGasKVStore(meter, mem) + require.Panics(t, func() { st.Set(keyFmt(1), valFmt(1)) }, "Expected out-of-gas") +} diff --git a/store/rootmultistore.go b/store/rootmultistore.go index 217e8eb140..11cebc22ea 100644 --- a/store/rootmultistore.go +++ b/store/rootmultistore.go @@ -183,6 +183,11 @@ func (rs *rootMultiStore) GetKVStore(key StoreKey) KVStore { return rs.stores[key].(KVStore) } +// Implements MultiStore. +func (rs *rootMultiStore) GetKVStoreWithGas(meter sdk.GasMeter, key StoreKey) KVStore { + return NewGasKVStore(meter, rs.GetKVStore(key)) +} + // getStoreByName will first convert the original name to // a special key, before looking up the CommitStore. // This is not exposed to the extensions (which will need the diff --git a/types/context.go b/types/context.go index 91c14373c2..50619f9b7e 100644 --- a/types/context.go +++ b/types/context.go @@ -69,7 +69,7 @@ func (c Context) Value(key interface{}) interface{} { // KVStore fetches a KVStore from the MultiStore. func (c Context) KVStore(key StoreKey) KVStore { - return NewGasKVStore(c.GasMeter(), c.multiStore().GetKVStore(key)) + return c.multiStore().GetKVStoreWithGas(c.GasMeter(), key) } //---------------------------------------- diff --git a/types/gaskvstore_test.go b/types/gaskvstore_test.go deleted file mode 100644 index 4ab9c15a15..0000000000 --- a/types/gaskvstore_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package types - -import ( - "testing" - - "github.com/stretchr/testify/require" - cmn "github.com/tendermint/tmlibs/common" - dbm "github.com/tendermint/tmlibs/db" -) - -func newGasKVStore() KVStore { - meter := NewGasMeter(1000) - mem := dbStoreAdapter{dbm.NewMemDB()} - return NewGasKVStore(meter, mem) -} - -func TestGasKVStoreBasic(t *testing.T) { - mem := dbStoreAdapter{dbm.NewMemDB()} - meter := NewGasMeter(1000) - st := NewGasKVStore(meter, mem) - - require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") - - mem.Set(keyFmt(1), valFmt(1)) - st.Set(keyFmt(1), valFmt(1)) - require.Equal(t, valFmt(1), st.Get(keyFmt(1))) -} - -func TestGasKVStoreOutOfGas(t *testing.T) { - mem := dbStoreAdapter{dbm.NewMemDB()} - meter := NewGasMeter(0) - st := NewGasKVStore(meter, mem) - require.Panics(t, func() { st.Set(keyFmt(1), valFmt(1)) }, "Expected out-of-gas") -} - -func keyFmt(i int) []byte { return bz(cmn.Fmt("key%0.8d", i)) } -func valFmt(i int) []byte { return bz(cmn.Fmt("value%0.8d", i)) } -func bz(s string) []byte { return []byte(s) } - -type dbStoreAdapter struct { - dbm.DB -} - -// Implements Store. -func (dbStoreAdapter) GetStoreType() StoreType { - return StoreTypeDB -} - -// Implements KVStore. -func (dsa dbStoreAdapter) CacheWrap() CacheWrap { - panic("unsupported") -} - -func (dsa dbStoreAdapter) SubspaceIterator(prefix []byte) Iterator { - return dsa.Iterator(prefix, PrefixEndBytes(prefix)) -} - -func (dsa dbStoreAdapter) ReverseSubspaceIterator(prefix []byte) Iterator { - return dsa.ReverseIterator(prefix, PrefixEndBytes(prefix)) -} - -// dbm.DB implements KVStore so we can CacheKVStore it. -var _ KVStore = dbStoreAdapter{dbm.DB(nil)} diff --git a/types/store.go b/types/store.go index f8367a1260..abf02ec071 100644 --- a/types/store.go +++ b/types/store.go @@ -49,6 +49,7 @@ type MultiStore interface { //nolint // Convenience for fetching substores. GetStore(StoreKey) Store GetKVStore(StoreKey) KVStore + GetKVStoreWithGas(GasMeter, StoreKey) KVStore } // From MultiStore.CacheMultiStore()....