refactor(store/v2): remove GasKV and Gas types (#18912)
This commit is contained in:
parent
d55985637e
commit
bb99dda6d0
261
store/gas.go
261
store/gas.go
@ -1,261 +0,0 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
// Gas defines type alias of uint64 for gas consumption. Gas is measured by the
|
||||
// SDK for store operations such as Get and Set calls. In addition, callers have
|
||||
// the ability to explicitly charge gas for costly operations such as signature
|
||||
// verification.
|
||||
type Gas = uint64
|
||||
|
||||
// Gas consumption descriptors.
|
||||
const (
|
||||
GasDescIterNextCostFlat = "IterNextFlat"
|
||||
GasDescValuePerByte = "ValuePerByte"
|
||||
GasDescWritePerByte = "WritePerByte"
|
||||
GasDescReadPerByte = "ReadPerByte"
|
||||
GasDescWriteCostFlat = "WriteFlat"
|
||||
GasDescReadCostFlat = "ReadFlat"
|
||||
GasDescHas = "Has"
|
||||
GasDescDelete = "Delete"
|
||||
)
|
||||
|
||||
type (
|
||||
// ErrorNegativeGasConsumed defines an error thrown when the amount of gas refunded
|
||||
// results in a negative gas consumed amount.
|
||||
ErrorNegativeGasConsumed struct {
|
||||
Descriptor string
|
||||
}
|
||||
|
||||
// ErrorOutOfGas defines an error thrown when an action results in out of gas.
|
||||
ErrorOutOfGas struct {
|
||||
Descriptor string
|
||||
}
|
||||
|
||||
// ErrorGasOverflow defines an error thrown when an action results gas consumption
|
||||
// unsigned integer overflow.
|
||||
ErrorGasOverflow struct {
|
||||
Descriptor string
|
||||
}
|
||||
)
|
||||
|
||||
func (e ErrorNegativeGasConsumed) Error() string {
|
||||
return fmt.Sprintf("negative gas consumed: %s", e.Descriptor)
|
||||
}
|
||||
|
||||
func (e ErrorOutOfGas) Error() string {
|
||||
return fmt.Sprintf("out of gas: %s", e.Descriptor)
|
||||
}
|
||||
|
||||
func (e ErrorGasOverflow) Error() string {
|
||||
return fmt.Sprintf("gas overflow: %s", e.Descriptor)
|
||||
}
|
||||
|
||||
// GasMeter defines an interface for gas consumption tracking.
|
||||
type GasMeter interface {
|
||||
// GasConsumed returns the amount of gas consumed so far.
|
||||
GasConsumed() Gas
|
||||
// GasConsumedToLimit returns the gas limit if gas consumed is past the limit,
|
||||
// otherwise it returns the consumed gas so far.
|
||||
GasConsumedToLimit() Gas
|
||||
// GasRemaining returns the gas left in the GasMeter.
|
||||
GasRemaining() Gas
|
||||
// Limit returns the gas limit (if any).
|
||||
Limit() Gas
|
||||
// ConsumeGas adds the given amount of gas to the gas consumed and should panic
|
||||
// if it overflows the gas limit (if any).
|
||||
ConsumeGas(amount Gas, descriptor string)
|
||||
// RefundGas will deduct the given amount from the gas consumed so far. If the
|
||||
// amount is greater than the gas consumed, the function should panic.
|
||||
RefundGas(amount Gas, descriptor string)
|
||||
// IsPastLimit returns <true> if the gas consumed so far is past the limit (if any),
|
||||
// otherwise it returns <false>.
|
||||
IsPastLimit() bool
|
||||
// IsOutOfGas returns <true> if the gas consumed so far is greater than or equal
|
||||
// to gas limit (if any), otherwise it returns <false>.
|
||||
IsOutOfGas() bool
|
||||
|
||||
fmt.Stringer
|
||||
}
|
||||
|
||||
// GasConfig defines gas cost for each operation on a KVStore.
|
||||
type GasConfig struct {
|
||||
// HasCost should reflect a fixed cost for a Has() call on a store.
|
||||
HasCost Gas
|
||||
// DeleteCost should reflect a fixed cost for a Delete() call on a store.
|
||||
DeleteCost Gas
|
||||
// ReadCostFlat should reflect a fixed cost for a Get() call on a store.
|
||||
ReadCostFlat Gas
|
||||
// ReadCostPerByte should reflect a fixed cost, per-byte on the key and value,
|
||||
// for a Get() call on a store. Note, this cost can also be used on iteration
|
||||
// seeks.
|
||||
ReadCostPerByte Gas
|
||||
// WriteCostFlat should reflect a fixed cost for a Set() call on a store.
|
||||
WriteCostFlat Gas
|
||||
// WriteCostPerByte should reflect a fixed cost, per-byte on the key and value,
|
||||
// for a Set() call on a store.
|
||||
WriteCostPerByte Gas
|
||||
// IterNextCostFlat should reflect a fixed cost for each call to Next() on an
|
||||
// iterator.
|
||||
IterNextCostFlat Gas
|
||||
}
|
||||
|
||||
// DefaultGasConfig returns a default GasConfig for gas metering.
|
||||
//
|
||||
// Note, these values are essentially arbitrary. They are not based on any specific
|
||||
// computation or measurements, but mainly reflect relative costs, i.e. writes
|
||||
// should be more expensive than reads.
|
||||
func DefaultGasConfig() GasConfig {
|
||||
return GasConfig{
|
||||
HasCost: 1000,
|
||||
ReadCostFlat: 1000,
|
||||
ReadCostPerByte: 3,
|
||||
DeleteCost: 1500,
|
||||
WriteCostFlat: 2000,
|
||||
WriteCostPerByte: 30,
|
||||
IterNextCostFlat: 30,
|
||||
}
|
||||
}
|
||||
|
||||
// defaultGasMeter defines a default implementation of a GasMeter.
|
||||
type defaultGasMeter struct {
|
||||
limit Gas
|
||||
consumed Gas
|
||||
}
|
||||
|
||||
// NewGasMeter returns a reference to a GasMeter with the provided limit.
|
||||
func NewGasMeter(limit Gas) GasMeter {
|
||||
return &defaultGasMeter{
|
||||
limit: limit,
|
||||
}
|
||||
}
|
||||
|
||||
func (gm *defaultGasMeter) GasConsumed() Gas {
|
||||
return gm.consumed
|
||||
}
|
||||
|
||||
// NOTE: This behavior should only be called when recovering from a panic when
|
||||
// BlockGasMeter consumes gas past the gas limit.
|
||||
func (gm *defaultGasMeter) GasConsumedToLimit() Gas {
|
||||
if gm.IsPastLimit() {
|
||||
return gm.limit
|
||||
}
|
||||
|
||||
return gm.consumed
|
||||
}
|
||||
|
||||
func (gm *defaultGasMeter) GasRemaining() Gas {
|
||||
if gm.IsPastLimit() {
|
||||
return 0
|
||||
}
|
||||
|
||||
return gm.limit - gm.consumed
|
||||
}
|
||||
|
||||
func (gm *defaultGasMeter) Limit() Gas {
|
||||
return gm.limit
|
||||
}
|
||||
|
||||
func (gm *defaultGasMeter) ConsumeGas(amount Gas, descriptor string) {
|
||||
newConsumed, overflow := addGasOverflow(gm.consumed, amount)
|
||||
if overflow {
|
||||
panic(ErrorGasOverflow{descriptor})
|
||||
}
|
||||
|
||||
if newConsumed > gm.limit {
|
||||
gm.consumed = math.MaxUint64
|
||||
panic(ErrorOutOfGas{descriptor})
|
||||
}
|
||||
|
||||
gm.consumed = newConsumed
|
||||
}
|
||||
|
||||
func (gm *defaultGasMeter) RefundGas(amount Gas, descriptor string) {
|
||||
if gm.consumed < amount {
|
||||
panic(ErrorNegativeGasConsumed{Descriptor: descriptor})
|
||||
}
|
||||
|
||||
gm.consumed -= amount
|
||||
}
|
||||
|
||||
func (gm *defaultGasMeter) IsPastLimit() bool {
|
||||
return gm.consumed > gm.limit
|
||||
}
|
||||
|
||||
func (gm *defaultGasMeter) IsOutOfGas() bool {
|
||||
return gm.consumed >= gm.limit
|
||||
}
|
||||
|
||||
func (gm *defaultGasMeter) String() string {
|
||||
return fmt.Sprintf("%T{limit: %d, consumed: %d}", gm, gm.limit, gm.consumed)
|
||||
}
|
||||
|
||||
// infiniteGasMeter defines a GasMeter with an infinite gas limit.
|
||||
type infiniteGasMeter struct {
|
||||
consumed Gas
|
||||
}
|
||||
|
||||
// NewInfiniteGasMeter returns a reference to a GasMeter with an infinite gas limit.
|
||||
func NewInfiniteGasMeter() GasMeter {
|
||||
return &infiniteGasMeter{
|
||||
consumed: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (gm *infiniteGasMeter) GasConsumed() Gas {
|
||||
return gm.consumed
|
||||
}
|
||||
|
||||
func (gm *infiniteGasMeter) GasConsumedToLimit() Gas {
|
||||
return gm.consumed
|
||||
}
|
||||
|
||||
func (*infiniteGasMeter) GasRemaining() Gas {
|
||||
return math.MaxUint64
|
||||
}
|
||||
|
||||
func (*infiniteGasMeter) Limit() Gas {
|
||||
return math.MaxUint64
|
||||
}
|
||||
|
||||
func (gm *infiniteGasMeter) ConsumeGas(amount Gas, descriptor string) {
|
||||
var overflow bool
|
||||
|
||||
gm.consumed, overflow = addGasOverflow(gm.consumed, amount)
|
||||
if overflow {
|
||||
panic(ErrorGasOverflow{descriptor})
|
||||
}
|
||||
}
|
||||
|
||||
func (gm *infiniteGasMeter) RefundGas(amount Gas, descriptor string) {
|
||||
if gm.consumed < amount {
|
||||
panic(ErrorNegativeGasConsumed{Descriptor: descriptor})
|
||||
}
|
||||
|
||||
gm.consumed -= amount
|
||||
}
|
||||
|
||||
func (*infiniteGasMeter) IsPastLimit() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (*infiniteGasMeter) IsOutOfGas() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (gm *infiniteGasMeter) String() string {
|
||||
return fmt.Sprintf("%T{consumed: %d}", gm, gm.consumed)
|
||||
}
|
||||
|
||||
// addGasOverflow performs the addition operation on two uint64 integers and
|
||||
// returns a boolean on whether or not the result overflows.
|
||||
func addGasOverflow(a, b Gas) (Gas, bool) {
|
||||
if math.MaxUint64-a < b {
|
||||
return 0, true
|
||||
}
|
||||
|
||||
return a + b, false
|
||||
}
|
||||
@ -1,63 +0,0 @@
|
||||
package gas
|
||||
|
||||
import "cosmossdk.io/store/v2"
|
||||
|
||||
var _ store.Iterator = (*iterator)(nil)
|
||||
|
||||
type iterator struct {
|
||||
gasMeter store.GasMeter
|
||||
gasConfig store.GasConfig
|
||||
parent store.Iterator
|
||||
}
|
||||
|
||||
func newIterator(parent store.Iterator, gm store.GasMeter, gc store.GasConfig) store.Iterator {
|
||||
return &iterator{
|
||||
parent: parent,
|
||||
gasConfig: gc,
|
||||
gasMeter: gm,
|
||||
}
|
||||
}
|
||||
|
||||
func (itr *iterator) Domain() ([]byte, []byte) {
|
||||
return itr.parent.Domain()
|
||||
}
|
||||
|
||||
func (itr *iterator) Valid() bool {
|
||||
return itr.parent.Valid()
|
||||
}
|
||||
|
||||
func (itr *iterator) Key() []byte {
|
||||
return itr.parent.Key()
|
||||
}
|
||||
|
||||
func (itr *iterator) Value() []byte {
|
||||
return itr.parent.Value()
|
||||
}
|
||||
|
||||
func (itr *iterator) Next() bool {
|
||||
itr.consumeGasSeek()
|
||||
return itr.parent.Next()
|
||||
}
|
||||
|
||||
func (itr *iterator) Close() {
|
||||
itr.parent.Close()
|
||||
}
|
||||
|
||||
func (itr *iterator) Error() error {
|
||||
return itr.parent.Error()
|
||||
}
|
||||
|
||||
// consumeGasSeek consumes a fixed amount of gas for each iteration step and a
|
||||
// variable gas cost based on the current key and value's length. This is called
|
||||
// prior to the iterator's Next() call.
|
||||
func (itr *iterator) consumeGasSeek() {
|
||||
if itr.Valid() {
|
||||
key := itr.Key()
|
||||
value := itr.Value()
|
||||
|
||||
itr.gasMeter.ConsumeGas(itr.gasConfig.ReadCostPerByte*store.Gas(len(key)), store.GasDescValuePerByte)
|
||||
itr.gasMeter.ConsumeGas(itr.gasConfig.ReadCostPerByte*store.Gas(len(value)), store.GasDescValuePerByte)
|
||||
}
|
||||
|
||||
itr.gasMeter.ConsumeGas(itr.gasConfig.IterNextCostFlat, store.GasDescIterNextCostFlat)
|
||||
}
|
||||
@ -1,89 +0,0 @@
|
||||
package gas
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"cosmossdk.io/store/v2"
|
||||
)
|
||||
|
||||
var _ store.BranchedKVStore = (*Store)(nil)
|
||||
|
||||
type Store struct {
|
||||
parent store.KVStore
|
||||
gasMeter store.GasMeter
|
||||
gasConfig store.GasConfig
|
||||
}
|
||||
|
||||
func New(p store.KVStore, gm store.GasMeter, gc store.GasConfig) store.BranchedKVStore {
|
||||
return &Store{
|
||||
parent: p,
|
||||
gasMeter: gm,
|
||||
gasConfig: gc,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Store) GetStoreKey() string {
|
||||
return s.parent.GetStoreKey()
|
||||
}
|
||||
|
||||
func (s *Store) GetStoreType() store.StoreType {
|
||||
return s.parent.GetStoreType()
|
||||
}
|
||||
|
||||
func (s *Store) Get(key []byte) []byte {
|
||||
s.gasMeter.ConsumeGas(s.gasConfig.ReadCostFlat, store.GasDescReadCostFlat)
|
||||
|
||||
value := s.parent.Get(key)
|
||||
s.gasMeter.ConsumeGas(s.gasConfig.ReadCostPerByte*store.Gas(len(key)), store.GasDescReadPerByte)
|
||||
s.gasMeter.ConsumeGas(s.gasConfig.ReadCostPerByte*store.Gas(len(value)), store.GasDescReadPerByte)
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func (s *Store) Has(key []byte) bool {
|
||||
s.gasMeter.ConsumeGas(s.gasConfig.HasCost, store.GasDescHas)
|
||||
return s.parent.Has(key)
|
||||
}
|
||||
|
||||
func (s *Store) Set(key, value []byte) {
|
||||
s.gasMeter.ConsumeGas(s.gasConfig.WriteCostFlat, store.GasDescWriteCostFlat)
|
||||
s.gasMeter.ConsumeGas(s.gasConfig.WriteCostPerByte*store.Gas(len(key)), store.GasDescWritePerByte)
|
||||
s.gasMeter.ConsumeGas(s.gasConfig.WriteCostPerByte*store.Gas(len(value)), store.GasDescWritePerByte)
|
||||
s.parent.Set(key, value)
|
||||
}
|
||||
|
||||
func (s *Store) Delete(key []byte) {
|
||||
s.gasMeter.ConsumeGas(s.gasConfig.DeleteCost, store.GasDescDelete)
|
||||
s.parent.Delete(key)
|
||||
}
|
||||
|
||||
func (s *Store) GetChangeset() *store.Changeset {
|
||||
return s.parent.GetChangeset()
|
||||
}
|
||||
|
||||
func (s *Store) Reset(toVersion uint64) error {
|
||||
return s.parent.Reset(toVersion)
|
||||
}
|
||||
|
||||
func (s *Store) Write() {
|
||||
if b, ok := s.parent.(store.BranchedKVStore); ok {
|
||||
b.Write()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Store) Branch() store.BranchedKVStore {
|
||||
panic(fmt.Sprintf("cannot call Branch() on %T", s))
|
||||
}
|
||||
|
||||
func (s *Store) BranchWithTrace(_ io.Writer, _ store.TraceContext) store.BranchedKVStore {
|
||||
panic(fmt.Sprintf("cannot call BranchWithTrace() on %T", s))
|
||||
}
|
||||
|
||||
func (s *Store) Iterator(start, end []byte) store.Iterator {
|
||||
return newIterator(s.parent.Iterator(start, end), s.gasMeter, s.gasConfig)
|
||||
}
|
||||
|
||||
func (s *Store) ReverseIterator(start, end []byte) store.Iterator {
|
||||
return newIterator(s.parent.ReverseIterator(start, end), s.gasMeter, s.gasConfig)
|
||||
}
|
||||
@ -1,111 +0,0 @@
|
||||
package gas_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"cosmossdk.io/store/v2"
|
||||
"cosmossdk.io/store/v2/kv/gas"
|
||||
"cosmossdk.io/store/v2/kv/mem"
|
||||
)
|
||||
|
||||
const (
|
||||
storeKey = "storeKey"
|
||||
gasLimit = store.Gas(1_000_000)
|
||||
)
|
||||
|
||||
type StoreTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
parent store.KVStore
|
||||
gasKVStore store.BranchedKVStore
|
||||
gasMeter store.GasMeter
|
||||
}
|
||||
|
||||
func TestStorageTestSuite(t *testing.T) {
|
||||
suite.Run(t, &StoreTestSuite{})
|
||||
}
|
||||
|
||||
func (s *StoreTestSuite) SetupTest() {
|
||||
s.parent = mem.New(storeKey)
|
||||
s.gasMeter = store.NewGasMeter(gasLimit)
|
||||
s.gasKVStore = gas.New(s.parent, s.gasMeter, store.DefaultGasConfig())
|
||||
}
|
||||
|
||||
func (s *StoreTestSuite) TearDownTest() {
|
||||
err := s.gasKVStore.Reset(1)
|
||||
s.Require().NoError(err)
|
||||
}
|
||||
|
||||
func (s *StoreTestSuite) TestGetStoreKey() {
|
||||
s.Require().Equal(s.parent.GetStoreKey(), s.gasKVStore.GetStoreKey())
|
||||
}
|
||||
|
||||
func (s *StoreTestSuite) TestGetStoreType() {
|
||||
s.Require().Equal(s.parent.GetStoreType(), s.gasKVStore.GetStoreType())
|
||||
}
|
||||
|
||||
func (s *StoreTestSuite) TestGet() {
|
||||
key, value := []byte("key"), []byte("value")
|
||||
s.parent.Set(key, value)
|
||||
|
||||
s.Require().Equal(value, s.gasKVStore.Get(key))
|
||||
s.Require().Equal(store.Gas(1024), s.gasMeter.GasConsumed())
|
||||
}
|
||||
|
||||
func (s *StoreTestSuite) TestHas() {
|
||||
key, value := []byte("key"), []byte("value")
|
||||
s.parent.Set(key, value)
|
||||
|
||||
s.Require().True(s.gasKVStore.Has(key))
|
||||
s.Require().Equal(store.Gas(1000), s.gasMeter.GasConsumed())
|
||||
}
|
||||
|
||||
func (s *StoreTestSuite) TestSet() {
|
||||
s.gasKVStore.Set([]byte("key"), []byte("value"))
|
||||
s.Require().Equal(store.Gas(2240), s.gasMeter.GasConsumed())
|
||||
}
|
||||
|
||||
func (s *StoreTestSuite) TestDelete() {
|
||||
key, value := []byte("key"), []byte("value")
|
||||
s.parent.Set(key, value)
|
||||
|
||||
s.gasKVStore.Delete(key)
|
||||
s.Require().Equal(store.Gas(1500), s.gasMeter.GasConsumed())
|
||||
}
|
||||
|
||||
func (s *StoreTestSuite) TestIterator() {
|
||||
for i := 0; i < 100; i++ {
|
||||
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099
|
||||
val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099
|
||||
s.parent.Set([]byte(key), []byte(val))
|
||||
}
|
||||
|
||||
itr := s.gasKVStore.Iterator(nil, nil)
|
||||
defer itr.Close()
|
||||
|
||||
for ; itr.Valid(); itr.Next() {
|
||||
_ = itr.Key()
|
||||
_ = itr.Value()
|
||||
}
|
||||
s.Require().Equal(store.Gas(6600), s.gasMeter.GasConsumed())
|
||||
}
|
||||
|
||||
func (s *StoreTestSuite) TestReverseIterator() {
|
||||
for i := 0; i < 100; i++ {
|
||||
key := fmt.Sprintf("key%03d", i) // key000, key001, ..., key099
|
||||
val := fmt.Sprintf("val%03d", i) // val000, val001, ..., val099
|
||||
s.parent.Set([]byte(key), []byte(val))
|
||||
}
|
||||
|
||||
itr := s.gasKVStore.ReverseIterator(nil, nil)
|
||||
defer itr.Close()
|
||||
|
||||
for ; itr.Valid(); itr.Next() {
|
||||
_ = itr.Key()
|
||||
_ = itr.Value()
|
||||
}
|
||||
s.Require().Equal(store.Gas(6600), s.gasMeter.GasConsumed())
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user