github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/store/gas/store.go (about)

     1  package gas
     2  
     3  import (
     4  	"github.com/gnolang/gno/tm2/pkg/store/types"
     5  	"github.com/gnolang/overflow"
     6  )
     7  
     8  var _ types.Store = &Store{}
     9  
    10  // Store applies gas tracking to an underlying Store. It implements the
    11  // Store interface.
    12  type Store struct {
    13  	gasMeter  types.GasMeter
    14  	gasConfig types.GasConfig
    15  	parent    types.Store
    16  }
    17  
    18  // New returns a reference to a new GasStore.
    19  func New(parent types.Store, gasMeter types.GasMeter, gasConfig types.GasConfig) *Store {
    20  	kvs := &Store{
    21  		gasMeter:  gasMeter,
    22  		gasConfig: gasConfig,
    23  		parent:    parent,
    24  	}
    25  	return kvs
    26  }
    27  
    28  // Implements Store.
    29  func (gs *Store) Get(key []byte) (value []byte) {
    30  	gs.gasMeter.ConsumeGas(gs.gasConfig.ReadCostFlat, types.GasReadCostFlatDesc)
    31  	value = gs.parent.Get(key)
    32  
    33  	gas := overflow.Mul64p(gs.gasConfig.ReadCostPerByte, types.Gas(len(value)))
    34  	gs.gasMeter.ConsumeGas(gas, types.GasReadPerByteDesc)
    35  
    36  	return value
    37  }
    38  
    39  // Implements Store.
    40  func (gs *Store) Set(key []byte, value []byte) {
    41  	types.AssertValidValue(value)
    42  	gs.gasMeter.ConsumeGas(gs.gasConfig.WriteCostFlat, types.GasWriteCostFlatDesc)
    43  
    44  	gas := overflow.Mul64p(gs.gasConfig.WriteCostPerByte, types.Gas(len(value)))
    45  	gs.gasMeter.ConsumeGas(gas, types.GasWritePerByteDesc)
    46  	gs.parent.Set(key, value)
    47  }
    48  
    49  // Implements Store.
    50  func (gs *Store) Has(key []byte) bool {
    51  	gs.gasMeter.ConsumeGas(gs.gasConfig.HasCost, types.GasHasDesc)
    52  	return gs.parent.Has(key)
    53  }
    54  
    55  // Implements Store.
    56  func (gs *Store) Delete(key []byte) {
    57  	// charge gas to prevent certain attack vectors even though space is being freed
    58  	gs.gasMeter.ConsumeGas(gs.gasConfig.DeleteCost, types.GasDeleteDesc)
    59  	gs.parent.Delete(key)
    60  }
    61  
    62  // Iterator implements the Store interface. It returns an iterator which
    63  // incurs a flat gas cost for seeking to the first key/value pair and a variable
    64  // gas cost based on the current value's length if the iterator is valid.
    65  func (gs *Store) Iterator(start, end []byte) types.Iterator {
    66  	return gs.iterator(start, end, true)
    67  }
    68  
    69  // ReverseIterator implements the Store interface. It returns a reverse
    70  // iterator which incurs a flat gas cost for seeking to the first key/value pair
    71  // and a variable gas cost based on the current value's length if the iterator
    72  // is valid.
    73  func (gs *Store) ReverseIterator(start, end []byte) types.Iterator {
    74  	return gs.iterator(start, end, false)
    75  }
    76  
    77  // Implements Store.
    78  func (gs *Store) CacheWrap() types.Store {
    79  	panic("cannot CacheWrap a gas.Store")
    80  }
    81  
    82  // Implements Store.
    83  func (gs *Store) Write() {
    84  	gs.parent.Write()
    85  }
    86  
    87  func (gs *Store) iterator(start, end []byte, ascending bool) types.Iterator {
    88  	var parent types.Iterator
    89  	if ascending {
    90  		parent = gs.parent.Iterator(start, end)
    91  	} else {
    92  		parent = gs.parent.ReverseIterator(start, end)
    93  	}
    94  
    95  	gi := newGasIterator(gs.gasMeter, gs.gasConfig, parent)
    96  	if gi.Valid() {
    97  		gi.(*gasIterator).consumeSeekGas()
    98  	}
    99  
   100  	return gi
   101  }
   102  
   103  type gasIterator struct {
   104  	gasMeter  types.GasMeter
   105  	gasConfig types.GasConfig
   106  	parent    types.Iterator
   107  }
   108  
   109  func newGasIterator(gasMeter types.GasMeter, gasConfig types.GasConfig, parent types.Iterator) types.Iterator {
   110  	return &gasIterator{
   111  		gasMeter:  gasMeter,
   112  		gasConfig: gasConfig,
   113  		parent:    parent,
   114  	}
   115  }
   116  
   117  // Implements Iterator.
   118  func (gi *gasIterator) Domain() (start []byte, end []byte) {
   119  	return gi.parent.Domain()
   120  }
   121  
   122  // Implements Iterator.
   123  func (gi *gasIterator) Valid() bool {
   124  	return gi.parent.Valid()
   125  }
   126  
   127  // Next implements the Iterator interface. It seeks to the next key/value pair
   128  // in the iterator. It incurs a flat gas cost for seeking and a variable gas
   129  // cost based on the current value's length if the iterator is valid.
   130  func (gi *gasIterator) Next() {
   131  	if gi.Valid() {
   132  		gi.consumeSeekGas()
   133  	}
   134  
   135  	gi.parent.Next()
   136  }
   137  
   138  // Key implements the Iterator interface. It returns the current key and it does
   139  // not incur any gas cost.
   140  func (gi *gasIterator) Key() (key []byte) {
   141  	key = gi.parent.Key()
   142  	return key
   143  }
   144  
   145  // Value implements the Iterator interface. It returns the current value and it
   146  // does not incur any gas cost.
   147  func (gi *gasIterator) Value() (value []byte) {
   148  	value = gi.parent.Value()
   149  	return value
   150  }
   151  
   152  // Implements Iterator.
   153  func (gi *gasIterator) Close() {
   154  	gi.parent.Close()
   155  }
   156  
   157  // consumeSeekGas consumes a flat gas cost for seeking and a variable gas cost
   158  // based on the current value's length.
   159  func (gi *gasIterator) consumeSeekGas() {
   160  	value := gi.Value()
   161  	gas := overflow.Mul64p(gi.gasConfig.ReadCostPerByte, types.Gas(len(value)))
   162  	gi.gasMeter.ConsumeGas(gi.gasConfig.IterNextCostFlat, types.GasIterNextCostFlatDesc)
   163  	gi.gasMeter.ConsumeGas(gas, types.GasValuePerByteDesc)
   164  }