-
Notifications
You must be signed in to change notification settings - Fork 3.7k
/
gas.go
181 lines (150 loc) · 3.88 KB
/
gas.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package types
import (
"fmt"
"math"
)
// Gas consumption descriptors.
const (
GasIterNextCostFlatDesc = "IterNextFlat"
GasValuePerByteDesc = "ValuePerByte"
GasWritePerByteDesc = "WritePerByte"
GasReadPerByteDesc = "ReadPerByte"
GasWriteCostFlatDesc = "WriteFlat"
GasReadCostFlatDesc = "ReadFlat"
GasHasDesc = "Has"
GasDeleteDesc = "Delete"
)
// Gas measured by the SDK
type Gas = uint64
// ErrorOutOfGas defines an error thrown when an action results in out of gas.
type ErrorOutOfGas struct {
Descriptor string
}
// ErrorGasOverflow defines an error thrown when an action results gas consumption
// unsigned integer overflow.
type ErrorGasOverflow struct {
Descriptor string
}
// GasMeter interface to track gas consumption
type GasMeter interface {
GasConsumed() Gas
GasConsumedToLimit() Gas
Limit() Gas
ConsumeGas(amount Gas, descriptor string)
IsPastLimit() bool
IsOutOfGas() bool
String() string
}
type basicGasMeter struct {
limit Gas
consumed Gas
}
// NewGasMeter returns a reference to a new basicGasMeter.
func NewGasMeter(limit Gas) GasMeter {
return &basicGasMeter{
limit: limit,
consumed: 0,
}
}
func (g *basicGasMeter) GasConsumed() Gas {
return g.consumed
}
func (g *basicGasMeter) Limit() Gas {
return g.limit
}
func (g *basicGasMeter) GasConsumedToLimit() Gas {
if g.IsPastLimit() {
return g.limit
}
return g.consumed
}
// addUint64Overflow performs the addition operation on two uint64 integers and
// returns a boolean on whether or not the result overflows.
func addUint64Overflow(a, b uint64) (uint64, bool) {
if math.MaxUint64-a < b {
return 0, true
}
return a + b, false
}
func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) {
var overflow bool
// TODO: Should we set the consumed field after overflow checking?
g.consumed, overflow = addUint64Overflow(g.consumed, amount)
if overflow {
panic(ErrorGasOverflow{descriptor})
}
if g.consumed > g.limit {
panic(ErrorOutOfGas{descriptor})
}
}
func (g *basicGasMeter) IsPastLimit() bool {
return g.consumed > g.limit
}
func (g *basicGasMeter) IsOutOfGas() bool {
return g.consumed >= g.limit
}
func (g *basicGasMeter) String() string {
return fmt.Sprintf("BasicGasMeter:\n limit: %d\n consumed: %d", g.limit, g.consumed)
}
type infiniteGasMeter struct {
consumed Gas
}
// NewInfiniteGasMeter returns a reference to a new infiniteGasMeter.
func NewInfiniteGasMeter() GasMeter {
return &infiniteGasMeter{
consumed: 0,
}
}
func (g *infiniteGasMeter) GasConsumed() Gas {
return g.consumed
}
func (g *infiniteGasMeter) GasConsumedToLimit() Gas {
return g.consumed
}
func (g *infiniteGasMeter) Limit() Gas {
return 0
}
func (g *infiniteGasMeter) ConsumeGas(amount Gas, descriptor string) {
var overflow bool
// TODO: Should we set the consumed field after overflow checking?
g.consumed, overflow = addUint64Overflow(g.consumed, amount)
if overflow {
panic(ErrorGasOverflow{descriptor})
}
}
func (g *infiniteGasMeter) IsPastLimit() bool {
return false
}
func (g *infiniteGasMeter) IsOutOfGas() bool {
return false
}
func (g *infiniteGasMeter) String() string {
return fmt.Sprintf("InfiniteGasMeter:\n consumed: %d", g.consumed)
}
// GasConfig defines gas cost for each operation on KVStores
type GasConfig struct {
HasCost Gas
DeleteCost Gas
ReadCostFlat Gas
ReadCostPerByte Gas
WriteCostFlat Gas
WriteCostPerByte Gas
IterNextCostFlat Gas
}
// KVGasConfig returns a default gas config for KVStores.
func KVGasConfig() GasConfig {
return GasConfig{
HasCost: 1000,
DeleteCost: 1000,
ReadCostFlat: 1000,
ReadCostPerByte: 3,
WriteCostFlat: 2000,
WriteCostPerByte: 30,
IterNextCostFlat: 30,
}
}
// TransientGasConfig returns a default gas config for TransientStores.
func TransientGasConfig() GasConfig {
// TODO: define gasconfig for transient stores
return KVGasConfig()
}