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 933 : #define FD_INTEGER_INFLATION_RATE_SCALE (1000000000000000UL)
9 4794 : #define FD_INTEGER_INFLATION_YEAR_NS (31556925993600000UL)
10 141633 : #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 1419 : #define FD_INTEGER_INFLATION_INITIAL_RATE (80000000000000UL)
14 2013 : #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 1419 : #define FD_INTEGER_INFLATION_TAPER_NUM (15U)
20 594 : #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 3831 : uint128 * out ) {
35 3831 : if( FD_UNLIKELY( !denominator || numerator<denominator ) ) return -1;
36 :
37 3825 : uint128 z_num = (uint128)numerator - (uint128)denominator;
38 3825 : uint128 z_den = (uint128)numerator + (uint128)denominator;
39 3825 : uint128 z_num_sq = z_num*z_num;
40 3825 : uint128 z_den_sq = z_den*z_den;
41 :
42 3825 : uint128 term = ( FD_INTEGER_INFLATION_DECAY_SCALE*z_num ) / z_den;
43 3825 : uint128 sum = 0;
44 126225 : for( ulong i=0UL; i<32UL; i++ ) {
45 122400 : sum += term / (uint128)( ( 2UL*i )+1UL );
46 122400 : if( i<31UL ) term = ( term*z_num_sq ) / z_den_sq;
47 122400 : }
48 :
49 3825 : *out = sum*2;
50 3825 : return 0;
51 3831 : }
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 3822 : uint128 * out ) {
61 3822 : uint128 x_sq;
62 3822 : if( FD_UNLIKELY( __builtin_mul_overflow( x, x, &x_sq ) ) ) return -1;
63 3816 : uint128 second = x_sq / ( 2*FD_INTEGER_INFLATION_DECAY_SCALE );
64 :
65 3816 : uint128 second_x;
66 3816 : if( FD_UNLIKELY( __builtin_mul_overflow( second, x, &second_x ) ) ) return -1;
67 3816 : uint128 third = second_x / ( 3*FD_INTEGER_INFLATION_DECAY_SCALE );
68 :
69 3816 : if( FD_UNLIKELY( second>x ) ) return -1;
70 3816 : uint128 decrement;
71 3816 : if( FD_UNLIKELY( __builtin_add_overflow( x-second, third, &decrement ) ) ) return -1;
72 3816 : if( FD_UNLIKELY( decrement>FD_INTEGER_INFLATION_DECAY_SCALE ) ) return -1;
73 :
74 3816 : *out = FD_INTEGER_INFLATION_DECAY_SCALE - decrement;
75 3816 : return 0;
76 3816 : }
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 3834 : uint128 * out ) {
84 3834 : if( FD_UNLIKELY( !ns_per_slot || !taper_den || taper_num>=taper_den ) ) return -1;
85 :
86 3819 : uint128 annual_exponent;
87 3819 : if( FD_UNLIKELY( approximate_logarithm( taper_den, taper_den-taper_num, &annual_exponent ) ) ) return -1;
88 :
89 3819 : uint128 product;
90 3819 : if( FD_UNLIKELY( __builtin_mul_overflow( annual_exponent, (uint128)ns_per_slot, &product ) ) ) return -1;
91 :
92 3819 : return approximate_exponential( product / (uint128)FD_INTEGER_INFLATION_YEAR_NS, out );
93 3819 : }
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 63342 : uint128 b ) {
99 63342 : if( a==FD_INTEGER_INFLATION_DECAY_SCALE ) return b;
100 59196 : if( b==FD_INTEGER_INFLATION_DECAY_SCALE ) return a;
101 59193 : return ( a*b )>>64;
102 59196 : }
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 3777 : uint128 * out ) {
109 3777 : if( FD_UNLIKELY( base>FD_INTEGER_INFLATION_DECAY_SCALE ) ) return -1;
110 3774 : uint128 result = FD_INTEGER_INFLATION_DECAY_SCALE;
111 53352 : while( slots ) {
112 49578 : if( slots&1UL ) result = mul_decay_floor( result, base );
113 49578 : slots >>= 1;
114 49578 : if( slots ) base = mul_decay_floor( base, base );
115 49578 : }
116 3774 : *out = result;
117 3774 : return 0;
118 3777 : }
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 3774 : ulong * out ) {
125 3774 : if( FD_UNLIKELY( anchor_rate>FD_INTEGER_INFLATION_RATE_SCALE || decay>FD_INTEGER_INFLATION_DECAY_SCALE ) ) return -1;
126 3768 : *out = (ulong)( ( (uint128)anchor_rate*decay )>>64 );
127 3768 : return 0;
128 3774 : }
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 3765 : ulong * out ) {
138 3765 : uint128 factor;
139 3765 : if( FD_UNLIKELY( decay_per_slot( ns_per_slot, taper_num, taper_den, &factor ) ) ) return -1;
140 3762 : uint128 decay;
141 3762 : if( FD_UNLIKELY( pow_decay_floor( factor, slots, &decay ) ) ) return -1;
142 3762 : return apply_decay_floor( anchor_rate, decay, out );
143 3762 : }
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 939 : ulong * out ) {
152 939 : if( FD_UNLIKELY( rate>FD_INTEGER_INFLATION_RATE_SCALE || !ns_per_slot ) ) return -1;
153 :
154 933 : uint128 annual = ( (uint128)capitalization*(uint128)rate ) / (uint128)FD_INTEGER_INFLATION_RATE_SCALE;
155 :
156 933 : uint128 reward;
157 933 : if( FD_UNLIKELY( __builtin_mul_overflow( annual, (uint128)slots_in_epoch, &reward ) ||
158 933 : __builtin_mul_overflow( reward, (uint128)ns_per_slot, &reward ) ) ) return -1;
159 927 : reward /= (uint128)FD_INTEGER_INFLATION_YEAR_NS;
160 927 : if( FD_UNLIKELY( reward>(uint128)ULONG_MAX ) ) return -1;
161 :
162 927 : *out = (ulong)reward;
163 927 : return 0;
164 927 : }
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 2022 : uint taper_num ) {
173 2022 : 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 2022 : fd_features_t const * features = &bank->f.features;
178 2022 : fd_epoch_schedule_t const * epoch_schedule = &bank->f.epoch_schedule;
179 :
180 2022 : ulong rate = anchor_rate;
181 2022 : ulong cursor = start_slot;
182 5757 : while( cursor<end_slot ) {
183 3735 : ulong ns_per_slot = fd_slot_params_at_slot( bank, cursor ).ns_per_slot;
184 3735 : ulong seg_end = fd_ulong_min( fd_slot_params_next_transition( features, epoch_schedule, cursor, ns_per_slot ), end_slot );
185 3735 : 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 3735 : cursor = seg_end;
190 3735 : }
191 2022 : return rate;
192 2022 : }
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 1419 : ulong inflation_start_slot ) {
198 1419 : fd_epoch_schedule_t const * epoch_schedule = &bank->f.epoch_schedule;
199 1419 : ulong end_slot = fd_epoch_slot0( epoch_schedule, fd_slot_to_epoch( epoch_schedule, bank->f.slot, NULL ) );
200 :
201 1419 : ulong rate;
202 1419 : if( FD_FEATURE_ACTIVE_BANK( bank, double_disinflation_rate ) ) {
203 594 : ulong anchor_slot = fd_ulong_min( bank->f.features.double_disinflation_rate, end_slot );
204 594 : ulong anchor_rate = integer_inflation_rate_between( bank, FD_INTEGER_INFLATION_INITIAL_RATE, inflation_start_slot, anchor_slot, FD_INTEGER_INFLATION_TAPER_NUM );
205 594 : anchor_rate = fd_ulong_max( anchor_rate, FD_INTEGER_INFLATION_TERMINAL_RATE );
206 594 : rate = integer_inflation_rate_between( bank, anchor_rate, anchor_slot, end_slot, FD_INTEGER_INFLATION_DOUBLE_DISINFLATION_TAPER_NUM );
207 825 : } else {
208 825 : rate = integer_inflation_rate_between( bank, FD_INTEGER_INFLATION_INITIAL_RATE, inflation_start_slot, end_slot, FD_INTEGER_INFLATION_TAPER_NUM );
209 825 : }
210 1419 : return fd_ulong_max( rate, FD_INTEGER_INFLATION_TERMINAL_RATE );
211 1419 : }
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 921 : ulong epoch ) {
219 921 : fd_epoch_schedule_t const * epoch_schedule = &bank->f.epoch_schedule;
220 :
221 921 : ulong rate = integer_inflation_rate( bank, inflation_start_slot );
222 921 : ulong ns_per_slot = fd_slot_params_at_slot( bank, fd_epoch_slot0( epoch_schedule, epoch ) ).ns_per_slot;
223 921 : ulong reward;
224 921 : 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 921 : ulong new_capitalization;
228 921 : 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 921 : return reward;
232 921 : }
|