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 }