LCOV - code coverage report
Current view: top level - flamenco/rewards - fd_integer_inflation.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 121 130 93.1 %
Date: 2026-11-12 04:25:03 Functions: 11 21 52.4 %

          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 : }

Generated by: LCOV version 1.14