From bb99dda6d0f3bc60045b26ee64a2b0e747c46515 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Tue, 2 Jan 2024 05:26:08 -0500 Subject: [PATCH] refactor(store/v2): remove GasKV and Gas types (#18912) --- store/gas.go | 261 ------------------------------------- store/kv/gas/iterator.go | 63 --------- store/kv/gas/store.go | 89 ------------- store/kv/gas/store_test.go | 111 ---------------- 4 files changed, 524 deletions(-) delete mode 100644 store/gas.go delete mode 100644 store/kv/gas/iterator.go delete mode 100644 store/kv/gas/store.go delete mode 100644 store/kv/gas/store_test.go diff --git a/store/gas.go b/store/gas.go deleted file mode 100644 index 9bd142e506..0000000000 --- a/store/gas.go +++ /dev/null @@ -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 if the gas consumed so far is past the limit (if any), - // otherwise it returns . - IsPastLimit() bool - // IsOutOfGas returns if the gas consumed so far is greater than or equal - // to gas limit (if any), otherwise it returns . - 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 -} diff --git a/store/kv/gas/iterator.go b/store/kv/gas/iterator.go deleted file mode 100644 index a8043863a6..0000000000 --- a/store/kv/gas/iterator.go +++ /dev/null @@ -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) -} diff --git a/store/kv/gas/store.go b/store/kv/gas/store.go deleted file mode 100644 index ef362f74db..0000000000 --- a/store/kv/gas/store.go +++ /dev/null @@ -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) -} diff --git a/store/kv/gas/store_test.go b/store/kv/gas/store_test.go deleted file mode 100644 index 1ea8efd5e7..0000000000 --- a/store/kv/gas/store_test.go +++ /dev/null @@ -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()) -}