Line data Source code
1 : #include "fd_integer_inflation.h"
2 : #include "../features/fd_features.h"
3 : #include "../runtime/fd_bank.h"
4 : #include "../runtime/fd_slot_params.h"
5 : #include "../runtime/sysvar/fd_sysvar_epoch_schedule.h"
6 :
7 : /* https://github.com/anza-xyz/agave/blob/9bcf93e368b5fb52c21677ed8c7f0360eb93b5b7/math-utils/src/inflation.rs#L6-L8 */
8 0 : #define FD_INTEGER_INFLATION_RATE_SCALE (1000000000000000UL)
9 0 : #define FD_INTEGER_INFLATION_YEAR_NS (31556925993600000UL)
10 0 : #define FD_INTEGER_INFLATION_DECAY_SCALE (((uint128)1UL)<<64)
11 :
12 : /* https://github.com/anza-xyz/agave/blob/9bcf93e368b5fb52c21677ed8c7f0360eb93b5b7/runtime/src/bank.rs#L261-L262 */
13 0 : #define FD_INTEGER_INFLATION_INITIAL_RATE (80000000000000UL)
14 0 : #define FD_INTEGER_INFLATION_TERMINAL_RATE (15000000000000UL)
15 :
16 : /* https://github.com/anza-xyz/agave/blob/9bcf93e368b5fb52c21677ed8c7f0360eb93b5b7/runtime/src/bank.rs#L3111
17 : https://github.com/anza-xyz/agave/blob/9bcf93e368b5fb52c21677ed8c7f0360eb93b5b7/runtime/src/bank.rs#L3138-L3143 */
18 : #define FD_INTEGER_INFLATION_TAPER_DEN (100U)
19 0 : #define FD_INTEGER_INFLATION_TAPER_NUM (15U)
20 0 : #define FD_INTEGER_INFLATION_DOUBLE_DISINFLATION_TAPER_NUM (30U)
21 :
22 : /* approximate_logarithm computes ln(numerator/denominator) scaled by
23 : FD_INTEGER_INFLATION_DECAY_SCALE, using the first 32 terms of the
24 : atanh series.
25 :
26 : approximate_logarithm calculates ln(numerator/denominator) using
27 : the first 32 terms of the atanh series. The terms are scaled by
28 : FD_INTEGER_INFLATION_DECAY_SCALE.
29 :
30 : https://github.com/anza-xyz/agave/blob/9bcf93e368b5fb52c21677ed8c7f0360eb93b5b7/math-utils/src/inflation.rs#L50-L77 */
31 : static int
32 : approximate_logarithm( uint numerator,
33 : uint denominator,
34 0 : uint128 * out ) {
35 0 : if( FD_UNLIKELY( !denominator || numerator<denominator ) ) return -1;
36 :
37 0 : uint128 z_num = (uint128)numerator - (uint128)denominator;
38 0 : uint128 z_den = (uint128)numerator + (uint128)denominator;
39 0 : uint128 z_num_sq = z_num*z_num;
40 0 : uint128 z_den_sq = z_den*z_den;
41 :
42 0 : uint128 term = ( FD_INTEGER_INFLATION_DECAY_SCALE*z_num ) / z_den;
43 0 : uint128 sum = 0;
44 0 : for( ulong i=0UL; i<32UL; i++ ) {
45 0 : sum += term / (uint128)( ( 2UL*i )+1UL );
46 0 : if( i<31UL ) term = ( term*z_num_sq ) / z_den_sq;
47 0 : }
48 :
49 0 : *out = sum*2;
50 0 : return 0;
51 0 : }
52 :
53 : /* approximate_exponential calculates an approximate value of e^-x
54 : using the first 4 terms of it's Taylor series. x and the result
55 : are scaled by FD_INTEGER_INFLATION_DECAY_SCALE.
56 :
57 : https://github.com/anza-xyz/agave/blob/9bcf93e368b5fb52c21677ed8c7f0360eb93b5b7/math-utils/src/inflation.rs#L80-L87 */
58 : static int
59 : approximate_exponential( uint128 x,
60 0 : uint128 * out ) {
61 0 : uint128 x_sq;
62 0 : if( FD_UNLIKELY( __builtin_mul_overflow( x, x, &x_sq ) ) ) return -1;
63 0 : uint128 second = x_sq / ( 2*FD_INTEGER_INFLATION_DECAY_SCALE );
64 :
65 0 : uint128 second_x;
66 0 : if( FD_UNLIKELY( __builtin_mul_overflow( second, x, &second_x ) ) ) return -1;
67 0 : uint128 third = second_x / ( 3*FD_INTEGER_INFLATION_DECAY_SCALE );
68 :
69 0 : if( FD_UNLIKELY( second>x ) ) return -1;
70 0 : uint128 decrement;
71 0 : if( FD_UNLIKELY( __builtin_add_overflow( x-second, third, &decrement ) ) ) return -1;
72 0 : if( FD_UNLIKELY( decrement>FD_INTEGER_INFLATION_DECAY_SCALE ) ) return -1;
73 :
74 0 : *out = FD_INTEGER_INFLATION_DECAY_SCALE - decrement;
75 0 : return 0;
76 0 : }
77 :
78 : /* https://github.com/anza-xyz/agave/blob/9bcf93e368b5fb52c21677ed8c7f0360eb93b5b7/math-utils/src/inflation.rs#L93-L101 */
79 : static int
80 : decay_per_slot( ulong ns_per_slot,
81 : uint taper_num,
82 : uint taper_den,
83 0 : uint128 * out ) {
84 0 : if( FD_UNLIKELY( !ns_per_slot || !taper_den || taper_num>=taper_den ) ) return -1;
85 :
86 0 : uint128 annual_exponent;
87 0 : if( FD_UNLIKELY( approximate_logarithm( taper_den, taper_den-taper_num, &annual_exponent ) ) ) return -1;
88 :
89 0 : uint128 product;
90 0 : if( FD_UNLIKELY( __builtin_mul_overflow( annual_exponent, (uint128)ns_per_slot, &product ) ) ) return -1;
91 :
92 0 : return approximate_exponential( product / (uint128)FD_INTEGER_INFLATION_YEAR_NS, out );
93 0 : }
94 :
95 : /* https://github.com/anza-xyz/agave/blob/9bcf93e368b5fb52c21677ed8c7f0360eb93b5b7/math-utils/src/inflation.rs#L105-L116 */
96 : static uint128
97 : mul_decay_floor( uint128 a,
98 0 : uint128 b ) {
99 0 : if( a==FD_INTEGER_INFLATION_DECAY_SCALE ) return b;
100 0 : if( b==FD_INTEGER_INFLATION_DECAY_SCALE ) return a;
101 0 : return ( a*b )>>64;
102 0 : }
103 :
104 : /* https://github.com/anza-xyz/agave/blob/9bcf93e368b5fb52c21677ed8c7f0360eb93b5b7/math-utils/src/inflation.rs#L120-L135 */
105 : static int
106 : pow_decay_floor( uint128 base,
107 : ulong slots,
108 0 : uint128 * out ) {
109 0 : if( FD_UNLIKELY( base>FD_INTEGER_INFLATION_DECAY_SCALE ) ) return -1;
110 0 : uint128 result = FD_INTEGER_INFLATION_DECAY_SCALE;
111 0 : while( slots ) {
112 0 : if( slots&1UL ) result = mul_decay_floor( result, base );
113 0 : slots >>= 1;
114 0 : if( slots ) base = mul_decay_floor( base, base );
115 0 : }
116 0 : *out = result;
117 0 : return 0;
118 0 : }
119 :
120 : /* https://github.com/anza-xyz/agave/blob/9bcf93e368b5fb52c21677ed8c7f0360eb93b5b7/math-utils/src/inflation.rs#L138-L146 */
121 : static int
122 : apply_decay_floor( ulong anchor_rate,
123 : uint128 decay,
124 0 : ulong * out ) {
125 0 : if( FD_UNLIKELY( anchor_rate>FD_INTEGER_INFLATION_RATE_SCALE || decay>FD_INTEGER_INFLATION_DECAY_SCALE ) ) return -1;
126 0 : *out = (ulong)( ( (uint128)anchor_rate*decay )>>64 );
127 0 : return 0;
128 0 : }
129 :
130 : /* https://github.com/anza-xyz/agave/blob/9bcf93e368b5fb52c21677ed8c7f0360eb93b5b7/math-utils/src/inflation.rs#L12-L22 */
131 : static int
132 : decayed_rate( ulong anchor_rate,
133 : uint taper_num,
134 : uint taper_den,
135 : ulong ns_per_slot,
136 : ulong slots,
137 0 : ulong * out ) {
138 0 : uint128 factor;
139 0 : if( FD_UNLIKELY( decay_per_slot( ns_per_slot, taper_num, taper_den, &factor ) ) ) return -1;
140 0 : uint128 decay;
141 0 : if( FD_UNLIKELY( pow_decay_floor( factor, slots, &decay ) ) ) return -1;
142 0 : return apply_decay_floor( anchor_rate, decay, out );
143 0 : }
144 :
145 : /* https://github.com/anza-xyz/agave/blob/9bcf93e368b5fb52c21677ed8c7f0360eb93b5b7/math-utils/src/inflation.rs#L26-L43 */
146 : static int
147 : epoch_reward( ulong capitalization,
148 : ulong rate,
149 : ulong slots_in_epoch,
150 : ulong ns_per_slot,
151 0 : ulong * out ) {
152 0 : if( FD_UNLIKELY( rate>FD_INTEGER_INFLATION_RATE_SCALE || !ns_per_slot ) ) return -1;
153 :
154 0 : uint128 annual = ( (uint128)capitalization*(uint128)rate ) / (uint128)FD_INTEGER_INFLATION_RATE_SCALE;
155 :
156 0 : uint128 reward;
157 0 : if( FD_UNLIKELY( __builtin_mul_overflow( annual, (uint128)slots_in_epoch, &reward ) ||
158 0 : __builtin_mul_overflow( reward, (uint128)ns_per_slot, &reward ) ) ) return -1;
159 0 : reward /= (uint128)FD_INTEGER_INFLATION_YEAR_NS;
160 0 : if( FD_UNLIKELY( reward>(uint128)ULONG_MAX ) ) return -1;
161 :
162 0 : *out = (ulong)reward;
163 0 : return 0;
164 0 : }
165 :
166 : /* https://github.com/anza-xyz/agave/blob/9bcf93e368b5fb52c21677ed8c7f0360eb93b5b7/runtime/src/bank.rs#L3089-L3120 */
167 : static ulong
168 : integer_inflation_rate_between( fd_bank_t const * bank,
169 : ulong anchor_rate,
170 : ulong start_slot,
171 : ulong end_slot,
172 0 : uint taper_num ) {
173 0 : if( FD_UNLIKELY( start_slot>end_slot ) ) {
174 0 : FD_LOG_ERR(( "inflation rate calculation failed (start_slot=%lu end_slot=%lu)", start_slot, end_slot ));
175 0 : }
176 :
177 0 : fd_features_t const * features = &bank->f.features;
178 0 : fd_epoch_schedule_t const * epoch_schedule = &bank->f.epoch_schedule;
179 :
180 0 : ulong rate = anchor_rate;
181 0 : ulong cursor = start_slot;
182 0 : while( cursor<end_slot ) {
183 0 : ulong ns_per_slot = fd_slot_params_at_slot( bank, cursor ).ns_per_slot;
184 0 : ulong seg_end = fd_ulong_min( fd_slot_params_next_transition( features, epoch_schedule, cursor, ns_per_slot ), end_slot );
185 0 : if( FD_UNLIKELY( decayed_rate( rate, taper_num, FD_INTEGER_INFLATION_TAPER_DEN, ns_per_slot, seg_end-cursor, &rate ) ) ) {
186 0 : FD_LOG_ERR(( "inflation rate calculation failed (rate=%lu taper=%u/%u ns_per_slot=%lu slots=%lu)",
187 0 : rate, taper_num, FD_INTEGER_INFLATION_TAPER_DEN, ns_per_slot, seg_end-cursor ));
188 0 : }
189 0 : cursor = seg_end;
190 0 : }
191 0 : return rate;
192 0 : }
193 :
194 : /* https://github.com/anza-xyz/agave/blob/9bcf93e368b5fb52c21677ed8c7f0360eb93b5b7/runtime/src/bank.rs#L3127-L3146 */
195 : static ulong
196 : integer_inflation_rate( fd_bank_t const * bank,
197 0 : ulong inflation_start_slot ) {
198 0 : fd_epoch_schedule_t const * epoch_schedule = &bank->f.epoch_schedule;
199 0 : ulong end_slot = fd_epoch_slot0( epoch_schedule, fd_slot_to_epoch( epoch_schedule, bank->f.slot, NULL ) );
200 :
201 0 : ulong rate;
202 0 : if( FD_FEATURE_ACTIVE_BANK( bank, double_disinflation_rate ) ) {
203 0 : ulong anchor_slot = fd_ulong_min( bank->f.features.double_disinflation_rate, end_slot );
204 0 : ulong anchor_rate = integer_inflation_rate_between( bank, FD_INTEGER_INFLATION_INITIAL_RATE, inflation_start_slot, anchor_slot, FD_INTEGER_INFLATION_TAPER_NUM );
205 0 : anchor_rate = fd_ulong_max( anchor_rate, FD_INTEGER_INFLATION_TERMINAL_RATE );
206 0 : rate = integer_inflation_rate_between( bank, anchor_rate, anchor_slot, end_slot, FD_INTEGER_INFLATION_DOUBLE_DISINFLATION_TAPER_NUM );
207 0 : } else {
208 0 : rate = integer_inflation_rate_between( bank, FD_INTEGER_INFLATION_INITIAL_RATE, inflation_start_slot, end_slot, FD_INTEGER_INFLATION_TAPER_NUM );
209 0 : }
210 0 : return fd_ulong_max( rate, FD_INTEGER_INFLATION_TERMINAL_RATE );
211 0 : }
212 :
213 : /* https://github.com/anza-xyz/agave/blob/9bcf93e368b5fb52c21677ed8c7f0360eb93b5b7/runtime/src/bank.rs#L3155-L3172 */
214 : ulong
215 : fd_integer_inflation_epoch_rewards( fd_bank_t const * bank,
216 : ulong inflation_start_slot,
217 : ulong capitalization,
218 0 : ulong epoch ) {
219 0 : fd_epoch_schedule_t const * epoch_schedule = &bank->f.epoch_schedule;
220 :
221 0 : ulong rate = integer_inflation_rate( bank, inflation_start_slot );
222 0 : ulong ns_per_slot = fd_slot_params_at_slot( bank, fd_epoch_slot0( epoch_schedule, epoch ) ).ns_per_slot;
223 0 : ulong reward;
224 0 : if( FD_UNLIKELY( epoch_reward( capitalization, rate, fd_epoch_slot_cnt( epoch_schedule, epoch ), ns_per_slot, &reward ) ) ) {
225 0 : FD_LOG_ERR(( "epoch reward calculation failed (capitalization=%lu rate=%lu epoch=%lu)", capitalization, rate, epoch ));
226 0 : }
227 0 : ulong new_capitalization;
228 0 : if( FD_UNLIKELY( __builtin_uaddl_overflow( capitalization, reward, &new_capitalization ) ) ) {
229 0 : FD_LOG_ERR(( "capitalization overflow (capitalization=%lu reward=%lu)", capitalization, reward ));
230 0 : }
231 0 : return reward;
232 0 : }
|