@@ -43,7 +43,7 @@ func (s State) AdvanceTime(target gas.Gas, seconds uint64) State {
43
43
}
44
44
45
45
// CostOf calculates how much to charge based on the dynamic fee mechanism for
46
- // [ seconds] .
46
+ // seconds.
47
47
//
48
48
// This implements the ACP-77 cost over time formula:
49
49
func (s State ) CostOf (c Config , seconds uint64 ) uint64 {
@@ -91,77 +91,46 @@ func (s State) CostOf(c Config, seconds uint64) uint64 {
91
91
return cost
92
92
}
93
93
94
- // SecondsUntil calculates the number of seconds that it would take to charge at
95
- // least [targetCost] based on the dynamic fee mechanism. The result is capped
96
- // at [ maxSeconds] .
97
- func (s State ) SecondsUntil (c Config , maxSeconds uint64 , targetCost uint64 ) uint64 {
94
+ // SecondsRemaining calculates the maximum number of seconds that a validator
95
+ // can pay fees before their fundsRemaining would be exhausted based on the
96
+ // dynamic fee mechanism. The result is capped at maxSeconds.
97
+ func (s State ) SecondsRemaining (c Config , maxSeconds uint64 , fundsRemaining uint64 ) uint64 {
98
98
// Because this function can divide by prices, we need to sanity check the
99
99
// parameters to avoid division by 0.
100
100
if c .MinPrice == 0 {
101
- if targetCost == 0 {
102
- return 0
103
- }
104
101
return maxSeconds
105
102
}
106
103
107
104
// If the current and target are the same, the price is constant.
108
105
if s .Current == c .Target {
109
- price := gas .CalculatePrice (c .MinPrice , s .Excess , c .ExcessConversionConstant )
110
- return secondsUntil (
111
- uint64 (price ),
112
- maxSeconds ,
113
- targetCost ,
114
- )
106
+ price := uint64 (gas .CalculatePrice (c .MinPrice , s .Excess , c .ExcessConversionConstant ))
107
+ seconds := fundsRemaining / price
108
+ return min (seconds , maxSeconds )
115
109
}
116
110
117
- var (
118
- cost uint64
119
- seconds uint64
120
- err error
121
- )
122
- for cost < targetCost && seconds < maxSeconds {
111
+ for seconds := uint64 (0 ); seconds < maxSeconds ; seconds ++ {
123
112
s = s .AdvanceTime (c .Target , 1 )
124
113
125
114
// Advancing the time is going to either hold excess constant,
126
115
// monotonically increase it, or monotonically decrease it. If it is
127
116
// equal to 0 after performing one of these operations, it is guaranteed
128
117
// to always remain 0.
129
118
if s .Excess == 0 {
130
- zeroExcessCost := targetCost - cost
131
- secondsWithZeroExcess := secondsUntil (
132
- uint64 (c .MinPrice ),
133
- maxSeconds ,
134
- zeroExcessCost ,
135
- )
136
-
119
+ secondsWithZeroExcess := fundsRemaining / uint64 (c .MinPrice )
137
120
totalSeconds , err := safemath .Add (seconds , secondsWithZeroExcess )
138
- if err != nil || totalSeconds >= maxSeconds {
121
+ if err != nil {
122
+ // This is technically unreachable, but makes the code more
123
+ // clearly correct.
139
124
return maxSeconds
140
125
}
141
- return totalSeconds
126
+ return min ( totalSeconds , maxSeconds )
142
127
}
143
128
144
- seconds ++
145
- price := gas .CalculatePrice (c .MinPrice , s .Excess , c .ExcessConversionConstant )
146
- cost , err = safemath .Add (cost , uint64 (price ))
147
- if err != nil {
129
+ price := uint64 (gas .CalculatePrice (c .MinPrice , s .Excess , c .ExcessConversionConstant ))
130
+ if price > fundsRemaining {
148
131
return seconds
149
132
}
133
+ fundsRemaining -= price
150
134
}
151
- return seconds
152
- }
153
-
154
- // Calculate the number of seconds that it would take to charge at least [cost]
155
- // at [price] every second. The result is capped at [maxSeconds].
156
- func secondsUntil (price uint64 , maxSeconds uint64 , cost uint64 ) uint64 {
157
- // Directly rounding up could cause an overflow. Instead we round down and
158
- // then check if we should have rounded up.
159
- secondsRoundedDown := cost / price
160
- if secondsRoundedDown >= maxSeconds {
161
- return maxSeconds
162
- }
163
- if cost % price == 0 {
164
- return secondsRoundedDown
165
- }
166
- return secondsRoundedDown + 1
135
+ return maxSeconds
167
136
}
0 commit comments