github.com/Finschia/finschia-sdk@v0.48.1/store/types/gas.go (about)

     1  package types
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  )
     7  
     8  // Gas consumption descriptors.
     9  const (
    10  	GasIterNextCostFlatDesc = "IterNextFlat"
    11  	GasValuePerByteDesc     = "ValuePerByte"
    12  	GasWritePerByteDesc     = "WritePerByte"
    13  	GasReadPerByteDesc      = "ReadPerByte"
    14  	GasWriteCostFlatDesc    = "WriteFlat"
    15  	GasReadCostFlatDesc     = "ReadFlat"
    16  	GasHasDesc              = "Has"
    17  	GasDeleteDesc           = "Delete"
    18  )
    19  
    20  // Gas measured by the SDK
    21  type Gas = uint64
    22  
    23  // ErrorNegativeGasConsumed defines an error thrown when the amount of gas refunded results in a
    24  // negative gas consumed amount.
    25  type ErrorNegativeGasConsumed struct {
    26  	Descriptor string
    27  }
    28  
    29  // ErrorOutOfGas defines an error thrown when an action results in out of gas.
    30  type ErrorOutOfGas struct {
    31  	Descriptor string
    32  }
    33  
    34  // ErrorGasOverflow defines an error thrown when an action results gas consumption
    35  // unsigned integer overflow.
    36  type ErrorGasOverflow struct {
    37  	Descriptor string
    38  }
    39  
    40  // GasMeter interface to track gas consumption
    41  type GasMeter interface {
    42  	GasConsumed() Gas
    43  	GasConsumedToLimit() Gas
    44  	Limit() Gas
    45  	ConsumeGas(amount Gas, descriptor string)
    46  	RefundGas(amount Gas, descriptor string)
    47  	IsPastLimit() bool
    48  	IsOutOfGas() bool
    49  	String() string
    50  }
    51  
    52  type basicGasMeter struct {
    53  	limit    Gas
    54  	consumed Gas
    55  }
    56  
    57  // NewGasMeter returns a reference to a new basicGasMeter.
    58  func NewGasMeter(limit Gas) GasMeter {
    59  	return &basicGasMeter{
    60  		limit:    limit,
    61  		consumed: 0,
    62  	}
    63  }
    64  
    65  func (g *basicGasMeter) GasConsumed() Gas {
    66  	return g.consumed
    67  }
    68  
    69  func (g *basicGasMeter) Limit() Gas {
    70  	return g.limit
    71  }
    72  
    73  func (g *basicGasMeter) GasConsumedToLimit() Gas {
    74  	if g.IsPastLimit() {
    75  		return g.limit
    76  	}
    77  	return g.consumed
    78  }
    79  
    80  // addUint64Overflow performs the addition operation on two uint64 integers and
    81  // returns a boolean on whether or not the result overflows.
    82  func addUint64Overflow(a, b uint64) (uint64, bool) {
    83  	if math.MaxUint64-a < b {
    84  		return 0, true
    85  	}
    86  
    87  	return a + b, false
    88  }
    89  
    90  func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) {
    91  	var overflow bool
    92  	g.consumed, overflow = addUint64Overflow(g.consumed, amount)
    93  	if overflow {
    94  		g.consumed = math.MaxUint64
    95  		panic(ErrorGasOverflow{descriptor})
    96  	}
    97  
    98  	if g.consumed > g.limit {
    99  		panic(ErrorOutOfGas{descriptor})
   100  	}
   101  }
   102  
   103  // RefundGas will deduct the given amount from the gas consumed. If the amount is greater than the
   104  // gas consumed, the function will panic.
   105  //
   106  // Use case: This functionality enables refunding gas to the transaction or block gas pools so that
   107  // EVM-compatible chains can fully support the go-ethereum StateDb interface.
   108  // See https://github.com/cosmos/cosmos-sdk/pull/9403 for reference.
   109  func (g *basicGasMeter) RefundGas(amount Gas, descriptor string) {
   110  	if g.consumed < amount {
   111  		panic(ErrorNegativeGasConsumed{Descriptor: descriptor})
   112  	}
   113  
   114  	g.consumed -= amount
   115  }
   116  
   117  func (g *basicGasMeter) IsPastLimit() bool {
   118  	return g.consumed > g.limit
   119  }
   120  
   121  func (g *basicGasMeter) IsOutOfGas() bool {
   122  	return g.consumed >= g.limit
   123  }
   124  
   125  func (g *basicGasMeter) String() string {
   126  	return fmt.Sprintf("BasicGasMeter:\n  limit: %d\n  consumed: %d", g.limit, g.consumed)
   127  }
   128  
   129  type infiniteGasMeter struct {
   130  	consumed Gas
   131  }
   132  
   133  // NewInfiniteGasMeter returns a reference to a new infiniteGasMeter.
   134  func NewInfiniteGasMeter() GasMeter {
   135  	return &infiniteGasMeter{
   136  		consumed: 0,
   137  	}
   138  }
   139  
   140  func (g *infiniteGasMeter) GasConsumed() Gas {
   141  	return g.consumed
   142  }
   143  
   144  func (g *infiniteGasMeter) GasConsumedToLimit() Gas {
   145  	return g.consumed
   146  }
   147  
   148  func (g *infiniteGasMeter) Limit() Gas {
   149  	return 0
   150  }
   151  
   152  func (g *infiniteGasMeter) ConsumeGas(amount Gas, descriptor string) {
   153  	var overflow bool
   154  	// TODO: Should we set the consumed field after overflow checking?
   155  	g.consumed, overflow = addUint64Overflow(g.consumed, amount)
   156  	if overflow {
   157  		panic(ErrorGasOverflow{descriptor})
   158  	}
   159  }
   160  
   161  // RefundGas will deduct the given amount from the gas consumed. If the amount is greater than the
   162  // gas consumed, the function will panic.
   163  //
   164  // Use case: This functionality enables refunding gas to the trasaction or block gas pools so that
   165  // EVM-compatible chains can fully support the go-ethereum StateDb interface.
   166  // See https://github.com/cosmos/cosmos-sdk/pull/9403 for reference.
   167  func (g *infiniteGasMeter) RefundGas(amount Gas, descriptor string) {
   168  	if g.consumed < amount {
   169  		panic(ErrorNegativeGasConsumed{Descriptor: descriptor})
   170  	}
   171  
   172  	g.consumed -= amount
   173  }
   174  
   175  func (g *infiniteGasMeter) IsPastLimit() bool {
   176  	return false
   177  }
   178  
   179  func (g *infiniteGasMeter) IsOutOfGas() bool {
   180  	return false
   181  }
   182  
   183  func (g *infiniteGasMeter) String() string {
   184  	return fmt.Sprintf("InfiniteGasMeter:\n  consumed: %d", g.consumed)
   185  }
   186  
   187  // GasConfig defines gas cost for each operation on KVStores
   188  type GasConfig struct {
   189  	HasCost          Gas
   190  	DeleteCost       Gas
   191  	ReadCostFlat     Gas
   192  	ReadCostPerByte  Gas
   193  	WriteCostFlat    Gas
   194  	WriteCostPerByte Gas
   195  	IterNextCostFlat Gas
   196  }
   197  
   198  // KVGasConfig returns a default gas config for KVStores.
   199  func KVGasConfig() GasConfig {
   200  	return GasConfig{
   201  		HasCost:          1000,
   202  		DeleteCost:       1000,
   203  		ReadCostFlat:     1000,
   204  		ReadCostPerByte:  3,
   205  		WriteCostFlat:    2000,
   206  		WriteCostPerByte: 30,
   207  		IterNextCostFlat: 30,
   208  	}
   209  }
   210  
   211  // TransientGasConfig returns a default gas config for TransientStores.
   212  func TransientGasConfig() GasConfig {
   213  	return GasConfig{
   214  		HasCost:          100,
   215  		DeleteCost:       100,
   216  		ReadCostFlat:     100,
   217  		ReadCostPerByte:  0,
   218  		WriteCostFlat:    200,
   219  		WriteCostPerByte: 3,
   220  		IterNextCostFlat: 3,
   221  	}
   222  }