LCOV - code coverage report
Current view: top level - flamenco/stakes - fd_stake_delegations.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 487 680 71.6 %
Date: 2026-08-13 04:56:22 Functions: 29 31 93.5 %

          Line data    Source code
       1             : #include "fd_stake_delegations.h"
       2             : #include "fd_stakes.h"
       3             : #include "../runtime/sysvar/fd_sysvar_stake_history.h"
       4             : 
       5             : #define POOL_NAME  root_pool
       6         234 : #define POOL_T     fd_stake_delegation_t
       7         138 : #define POOL_NEXT  next_
       8             : #define POOL_IDX_T uint
       9             : #define POOL_LAZY  1
      10             : #include "../../util/tmpl/fd_pool.c"
      11             : 
      12             : #define MAP_NAME               root_map
      13             : #define MAP_KEY_T              fd_pubkey_t
      14             : #define MAP_ELE_T              fd_stake_delegation_t
      15        4074 : #define MAP_KEY                stake_account
      16         717 : #define MAP_KEY_EQ(k0,k1)      (fd_pubkey_eq( k0, k1 ))
      17        8865 : #define MAP_KEY_HASH(key,seed) (fd_accdb_hash( key->uc, seed ))
      18        4227 : #define MAP_NEXT               next_
      19       17412 : #define MAP_IDX_T              uint
      20             : #include "../../util/tmpl/fd_map_chain.c"
      21             : 
      22             : #define MAP_NAME               fork_map
      23             : #define MAP_KEY_T              fd_pubkey_t
      24             : #define MAP_ELE_T              fd_stake_delegation_t
      25         474 : #define MAP_KEY                stake_account
      26          93 : #define MAP_KEY_EQ(k0,k1)      (fd_pubkey_eq( k0, k1 ))
      27        1044 : #define MAP_KEY_HASH(key,seed) (fd_accdb_hash( key->uc, seed ))
      28        1320 : #define MAP_NEXT               next_
      29       70269 : #define MAP_IDX_T              uint
      30             : #include "../../util/tmpl/fd_map_chain.c"
      31             : 
      32             : #define POOL_NAME  delta_pool
      33         234 : #define POOL_T     fd_stake_delegation_t
      34         546 : #define POOL_NEXT  next_
      35             : #define POOL_IDX_T uint
      36             : #define POOL_LAZY  1
      37             : #include "../../util/tmpl/fd_pool.c"
      38             : 
      39             : struct fork_pool_ele { ushort next; };
      40             : typedef struct fork_pool_ele fork_pool_ele_t;
      41             : 
      42             : #define POOL_NAME  fork_pool
      43         234 : #define POOL_T     fork_pool_ele_t
      44             : #define POOL_IDX_T ushort
      45             : #include "../../util/tmpl/fd_pool.c"
      46             : 
      47             : #define POOL_NAME  pubkey_pool
      48         234 : #define POOL_T     fd_stake_delegation_ref_t
      49          75 : #define POOL_NEXT  next_
      50             : #define POOL_IDX_T uint
      51             : #define POOL_LAZY  1
      52             : #include "../../util/tmpl/fd_pool.c"
      53             : 
      54             : #define MAP_NAME               pubkey_map
      55             : #define MAP_KEY_T              fd_pubkey_t
      56             : #define MAP_ELE_T              fd_stake_delegation_ref_t
      57        4110 : #define MAP_KEY                stake_account
      58         843 : #define MAP_KEY_EQ(k0,k1)      (fd_pubkey_eq( k0, k1 ))
      59        8964 : #define MAP_KEY_HASH(key,seed) (fd_accdb_hash( key->uc, seed ))
      60        4299 : #define MAP_NEXT               next_
      61       17805 : #define MAP_IDX_T              uint
      62             : #include "../../util/tmpl/fd_map_chain.c"
      63             : 
      64             : /* Internal getters for base map + pool */
      65             : 
      66             : static inline fd_stake_delegation_t *
      67       10575 : get_root_pool( fd_stake_delegations_t const * stake_delegations ) {
      68       10575 :   return fd_type_pun( (uchar *)stake_delegations + stake_delegations->pool_offset_ );
      69       10575 : }
      70             : 
      71             : static inline root_map_t *
      72        9183 : get_root_map( fd_stake_delegations_t const * stake_delegations ) {
      73        9183 :   return fd_type_pun( (uchar *)stake_delegations + stake_delegations->map_offset_ );
      74        9183 : }
      75             : 
      76             : /* Internal getters for delta pool + fork structures */
      77             : 
      78             : static inline fd_stake_delegation_t *
      79        7908 : get_delta_pool( fd_stake_delegations_t const * stake_delegations ) {
      80        7908 :   return fd_type_pun( (uchar *)stake_delegations + stake_delegations->delta_pool_offset_ );
      81        7908 : }
      82             : 
      83             : static inline fork_pool_ele_t *
      84        9384 : get_fork_pool( fd_stake_delegations_t const * stake_delegations ) {
      85        9384 :   return fd_type_pun( (uchar *)stake_delegations + stake_delegations->fork_pool_offset_ );
      86        9384 : }
      87             : 
      88             : static inline fork_map_t *
      89             : get_fork_map( fd_stake_delegations_t const * stake_delegations,
      90       65862 :               ushort                         fork_idx ) {
      91       65862 :   ulong map_footprint = fork_map_footprint( FD_STAKE_DELEGATIONS_FORK_MAP_CHAIN_CNT );
      92       65862 :   return fd_type_pun( (uchar *)stake_delegations + stake_delegations->fork_map_offset_ + (ulong)fork_idx*map_footprint );
      93       65862 : }
      94             : 
      95             : static inline fd_stake_delegation_ref_t *
      96        8769 : get_pubkey_pool( fd_stake_delegations_t const * stake_delegations ) {
      97        8769 :   return fd_type_pun( (uchar *)stake_delegations + stake_delegations->pubkey_pool_offset_ );
      98        8769 : }
      99             : 
     100             : static inline pubkey_map_t *
     101        8724 : get_pubkey_map( fd_stake_delegations_t const * stake_delegations ) {
     102        8724 :   return fd_type_pun( (uchar *)stake_delegations + stake_delegations->pubkey_map_offset_ );
     103        8724 : }
     104             : 
     105             : static void
     106             : pubkey_ref_acquire( fd_stake_delegations_t * stake_delegations,
     107        4485 :                     fd_pubkey_t const *      stake_account ) {
     108        4485 :   fd_stake_delegation_ref_t * pool = get_pubkey_pool( stake_delegations );
     109        4485 :   pubkey_map_t *              map  = get_pubkey_map( stake_delegations );
     110             : 
     111        4485 :   fd_stake_delegation_ref_t * ref = pubkey_map_ele_query( map, stake_account, NULL, pool );
     112        4485 :   if( FD_UNLIKELY( !ref ) ) {
     113        4110 :     FD_CHECK_CRIT( pubkey_pool_free( pool ), "no free entries in stake delegation pubkey pool" );
     114        4110 :     ref                = pubkey_pool_ele_acquire( pool );
     115        4110 :     ref->stake_account = *stake_account;
     116        4110 :     ref->refcnt        = 0U;
     117        4110 :     stake_delegations->pubkey_idx_wmk_ = fd_ulong_max( stake_delegations->pubkey_idx_wmk_, pubkey_pool_idx( pool, ref )+1UL );
     118        4110 :     FD_CHECK_CRIT( pubkey_map_ele_insert( map, ref, pool ), "unable to insert into stake delegation pubkey map" );
     119        4110 :   }
     120        4485 :   ref->refcnt++;
     121        4485 : }
     122             : 
     123             : static void
     124             : pubkey_ref_release( fd_stake_delegations_t * stake_delegations,
     125         345 :                     fd_pubkey_t const *      stake_account ) {
     126         345 :   if( FD_UNLIKELY( stake_delegations->pubkey_fallback ) ) return;
     127             : 
     128         309 :   fd_stake_delegation_ref_t * pool = get_pubkey_pool( stake_delegations );
     129         309 :   pubkey_map_t *              map  = get_pubkey_map( stake_delegations );
     130             : 
     131         309 :   fd_stake_delegation_ref_t * ref = pubkey_map_ele_query( map, stake_account, NULL, pool );
     132         309 :   if( FD_UNLIKELY( !ref ) ) return;
     133         309 :   if( FD_UNLIKELY( !ref->refcnt ) ) return;
     134             : 
     135         309 :   if( FD_LIKELY( !--ref->refcnt ) ) {
     136          60 :     pubkey_map_ele_remove( map, stake_account, NULL, pool );
     137          60 :     pubkey_pool_ele_release( pool, ref );
     138          60 :   }
     139         309 : }
     140             : 
     141             : static void
     142             : pubkey_fallback_enter( fd_stake_delegations_t * stake_delegations,
     143           6 :                        fd_pubkey_t const *      stake_account ) {
     144           6 :   if( FD_UNLIKELY( !stake_delegations->pubkey_fallback ) ) {
     145           6 :     FD_LOG_WARNING(( "stake delegation pool exhausted at %lu stake accounts; falling back to "
     146           6 :                      "resolving stake delegations from the accounts database at the epoch boundary",
     147           6 :                      fd_stake_delegations_pubkey_cnt( stake_delegations ) ));
     148           6 :     stake_delegations->pubkey_fallback = 1;
     149           6 :   }
     150           6 :   pubkey_ref_acquire( stake_delegations, stake_account );
     151           6 : }
     152             : 
     153             : ulong
     154        3795 : fd_stake_delegations_align( void ) {
     155        3795 :   return FD_STAKE_DELEGATIONS_ALIGN;
     156        3795 : }
     157             : 
     158             : ulong
     159             : fd_stake_delegations_footprint( ulong max_stake_accounts,
     160             :                                 ulong max_fallback_stake_accounts,
     161             :                                 ulong expected_stake_accounts,
     162         612 :                                 ulong max_live_slots ) {
     163             : 
     164         612 :   ulong map_chain_cnt    = root_map_chain_cnt_est( expected_stake_accounts );
     165         612 :   ulong pubkey_max       = max_fallback_stake_accounts;
     166         612 :   ulong pubkey_chain_cnt = pubkey_map_chain_cnt_est( expected_stake_accounts );
     167             : 
     168         612 :   ulong l = FD_LAYOUT_INIT;
     169         612 :   l = FD_LAYOUT_APPEND( l, fd_stake_delegations_align(), sizeof(fd_stake_delegations_t) );
     170         612 :   l = FD_LAYOUT_APPEND( l, root_pool_align(),            root_pool_footprint( max_stake_accounts ) );
     171         612 :   l = FD_LAYOUT_APPEND( l, root_map_align(),             root_map_footprint( map_chain_cnt ) );
     172         612 :   l = FD_LAYOUT_APPEND( l, delta_pool_align(),           delta_pool_footprint( max_stake_accounts ) );
     173         612 :   l = FD_LAYOUT_APPEND( l, fork_pool_align(),            fork_pool_footprint( max_live_slots ) );
     174         612 :   l = FD_LAYOUT_APPEND( l, fork_map_align(),             max_live_slots*fork_map_footprint( FD_STAKE_DELEGATIONS_FORK_MAP_CHAIN_CNT ) );
     175         612 :   l = FD_LAYOUT_APPEND( l, pubkey_pool_align(),          pubkey_pool_footprint( pubkey_max ) );
     176         612 :   l = FD_LAYOUT_APPEND( l, pubkey_map_align(),           pubkey_map_footprint( pubkey_chain_cnt ) );
     177             : 
     178         612 :   return FD_LAYOUT_FINI( l, fd_stake_delegations_align() );
     179         612 : }
     180             : 
     181             : void *
     182             : fd_stake_delegations_new( void * mem,
     183             :                           ulong  seed,
     184             :                           ulong  max_stake_accounts,
     185             :                           ulong  max_fallback_stake_accounts,
     186             :                           ulong  expected_stake_accounts,
     187         123 :                           ulong  max_live_slots ) {
     188         123 :   if( FD_UNLIKELY( !mem ) ) {
     189           3 :     FD_LOG_WARNING(( "NULL mem" ));
     190           3 :     return NULL;
     191           3 :   }
     192             : 
     193         120 :   if( FD_UNLIKELY( !max_stake_accounts ) ) {
     194           3 :     FD_LOG_WARNING(( "max_stake_accounts is 0" ));
     195           3 :     return NULL;
     196           3 :   }
     197             : 
     198         117 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_stake_delegations_align() ) ) ) {
     199           0 :     FD_LOG_WARNING(( "misaligned mem" ));
     200           0 :     return NULL;
     201           0 :   }
     202             : 
     203         117 :   if( FD_UNLIKELY( max_live_slots>FD_STAKE_DELEGATIONS_FORK_MAX ) ) {
     204           0 :     FD_LOG_WARNING(( "max_live_slots is too large" ));
     205           0 :     return NULL;
     206           0 :   }
     207             : 
     208         117 :   ulong map_chain_cnt    = root_map_chain_cnt_est( expected_stake_accounts );
     209         117 :   ulong pubkey_max       = max_fallback_stake_accounts;
     210         117 :   ulong pubkey_chain_cnt = pubkey_map_chain_cnt_est( expected_stake_accounts );
     211             : 
     212         117 :   FD_SCRATCH_ALLOC_INIT( l, mem );
     213         117 :   fd_stake_delegations_t * stake_delegations = FD_SCRATCH_ALLOC_APPEND( l, fd_stake_delegations_align(), sizeof(fd_stake_delegations_t) );
     214         117 :   void *                   pool_mem          = FD_SCRATCH_ALLOC_APPEND( l, root_pool_align(),            root_pool_footprint( max_stake_accounts ) );
     215         117 :   void *                   map_mem           = FD_SCRATCH_ALLOC_APPEND( l, root_map_align(),             root_map_footprint( map_chain_cnt ) );
     216         117 :   void *                   delta_pool_mem    = FD_SCRATCH_ALLOC_APPEND( l, delta_pool_align(),           delta_pool_footprint( max_stake_accounts ) );
     217         117 :   void *                   fork_pool_mem     = FD_SCRATCH_ALLOC_APPEND( l, fork_pool_align(),            fork_pool_footprint( max_live_slots ) );
     218         117 :   void *                   fork_map_mem      = FD_SCRATCH_ALLOC_APPEND( l, fork_map_align(),             max_live_slots*fork_map_footprint( FD_STAKE_DELEGATIONS_FORK_MAP_CHAIN_CNT ) );
     219         117 :   void *                   pubkey_pool_mem   = FD_SCRATCH_ALLOC_APPEND( l, pubkey_pool_align(),          pubkey_pool_footprint( pubkey_max ) );
     220         117 :   void *                   pubkey_map_mem    = FD_SCRATCH_ALLOC_APPEND( l, pubkey_map_align(),           pubkey_map_footprint( pubkey_chain_cnt ) );
     221        1926 :   for( ushort i=0; i<(ushort)max_live_slots; i++ ) {
     222        1809 :     void * fork_map_mem_i = (uchar *)fork_map_mem + (ulong)i*fork_map_footprint( FD_STAKE_DELEGATIONS_FORK_MAP_CHAIN_CNT );
     223        1809 :     fork_map_t * map = fork_map_join( fork_map_new( fork_map_mem_i, FD_STAKE_DELEGATIONS_FORK_MAP_CHAIN_CNT, seed ) );
     224        1809 :     if( FD_UNLIKELY( !map ) ) {
     225           0 :       FD_LOG_WARNING(( "Failed to create fork map" ));
     226           0 :       return NULL;
     227           0 :     }
     228        1809 :   }
     229             : 
     230         117 :   if( FD_UNLIKELY( FD_SCRATCH_ALLOC_FINI( l, fd_stake_delegations_align() )!=(ulong)mem+fd_stake_delegations_footprint( max_stake_accounts, max_fallback_stake_accounts, expected_stake_accounts, max_live_slots ) ) ) {
     231           0 :     FD_LOG_WARNING(( "fd_stake_delegations_new: bad layout" ));
     232           0 :     return NULL;
     233           0 :   }
     234             : 
     235         117 :   fd_stake_delegation_t * root_pool = root_pool_join( root_pool_new( pool_mem, max_stake_accounts ) );
     236         117 :   if( FD_UNLIKELY( !root_pool ) ) {
     237           0 :     FD_LOG_WARNING(( "Failed to create stake delegations pool" ));
     238           0 :     return NULL;
     239           0 :   }
     240             : 
     241         117 :   root_map_t * root_map = root_map_join( root_map_new( map_mem, map_chain_cnt, seed ) );
     242         117 :   if( FD_UNLIKELY( !root_map ) ) {
     243           0 :     FD_LOG_WARNING(( "Failed to create stake delegations map" ));
     244           0 :     return NULL;
     245           0 :   }
     246             : 
     247         117 :   fd_stake_delegation_t * delta_pool = delta_pool_join( delta_pool_new( delta_pool_mem, max_stake_accounts ) );
     248         117 :   if( FD_UNLIKELY( !delta_pool ) ) {
     249           0 :     FD_LOG_WARNING(( "Failed to create stake delegation delta pool" ));
     250           0 :     return NULL;
     251           0 :   }
     252             : 
     253         117 :   fork_pool_ele_t * fork_pool = fork_pool_join( fork_pool_new( fork_pool_mem, max_live_slots ) );
     254         117 :   if( FD_UNLIKELY( !fork_pool ) ) {
     255           0 :     FD_LOG_WARNING(( "Failed to create fork pool" ));
     256           0 :     return NULL;
     257           0 :   }
     258             : 
     259         117 :   fd_stake_delegation_ref_t * pubkey_pool = pubkey_pool_join( pubkey_pool_new( pubkey_pool_mem, pubkey_max ) );
     260         117 :   if( FD_UNLIKELY( !pubkey_pool ) ) {
     261           0 :     FD_LOG_WARNING(( "Failed to create stake delegation pubkey pool" ));
     262           0 :     return NULL;
     263           0 :   }
     264             : 
     265         117 :   pubkey_map_t * pubkey_map = pubkey_map_join( pubkey_map_new( pubkey_map_mem, pubkey_chain_cnt, seed ) );
     266         117 :   if( FD_UNLIKELY( !pubkey_map ) ) {
     267           0 :     FD_LOG_WARNING(( "Failed to create stake delegation pubkey map" ));
     268           0 :     return NULL;
     269           0 :   }
     270             : 
     271         117 :   stake_delegations->max_stake_accounts_      = max_stake_accounts;
     272         117 :   stake_delegations->expected_stake_accounts_ = expected_stake_accounts;
     273         117 :   stake_delegations->pool_offset_             = (ulong)root_pool - (ulong)mem;
     274         117 :   stake_delegations->map_offset_              = (ulong)root_map - (ulong)mem;
     275         117 :   stake_delegations->delta_pool_offset_       = (ulong)delta_pool - (ulong)mem;
     276         117 :   stake_delegations->fork_pool_offset_        = (ulong)fork_pool - (ulong)mem;
     277         117 :   stake_delegations->fork_map_offset_         = (ulong)fork_map_mem - (ulong)mem;
     278         117 :   stake_delegations->pubkey_pool_offset_      = (ulong)pubkey_pool - (ulong)mem;
     279         117 :   stake_delegations->pubkey_map_offset_       = (ulong)pubkey_map - (ulong)mem;
     280         117 :   stake_delegations->max_pubkeys_             = pubkey_max;
     281         117 :   stake_delegations->pubkey_idx_wmk_          = 0UL;
     282             : 
     283         117 :   stake_delegations->effective_stake    = 0UL;
     284         117 :   stake_delegations->activating_stake   = 0UL;
     285         117 :   stake_delegations->deactivating_stake = 0UL;
     286         117 :   stake_delegations->pool_idx_wmk_      = 0UL;
     287         117 :   stake_delegations->pubkey_fallback    = 0;
     288         117 :   stake_delegations->fp_warmed_awarded  = 0;
     289             : 
     290         117 :   fd_rwlock_new( &stake_delegations->lock );
     291             : 
     292         117 :   FD_COMPILER_MFENCE();
     293         117 :   FD_VOLATILE( stake_delegations->magic ) = FD_STAKE_DELEGATIONS_MAGIC;
     294         117 :   FD_COMPILER_MFENCE();
     295             : 
     296         117 :   return mem;
     297         117 : }
     298             : 
     299             : fd_stake_delegations_t *
     300         123 : fd_stake_delegations_join( void * mem ) {
     301         123 :   if( FD_UNLIKELY( !mem ) ) {
     302           3 :     FD_LOG_WARNING(( "NULL mem" ));
     303           3 :     return NULL;
     304           3 :   }
     305             : 
     306         120 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_stake_delegations_align() ) ) ) {
     307           0 :     FD_LOG_WARNING(( "misaligned mem" ));
     308           0 :     return NULL;
     309           0 :   }
     310             : 
     311         120 :   fd_stake_delegations_t * stake_delegations = (fd_stake_delegations_t *)mem;
     312             : 
     313         120 :   if( FD_UNLIKELY( stake_delegations->magic!=FD_STAKE_DELEGATIONS_MAGIC ) ) {
     314           3 :     FD_LOG_WARNING(( "Invalid stake delegations magic" ));
     315           3 :     return NULL;
     316           3 :   }
     317             : 
     318         117 :   return stake_delegations;
     319         120 : }
     320             : 
     321             : void
     322        3930 : fd_stake_delegations_reset( fd_stake_delegations_t * stake_delegations ) {
     323        3930 :   fd_rwlock_write( &stake_delegations->lock );
     324        3930 :   root_pool_reset( get_root_pool( stake_delegations ) );
     325        3930 :   root_map_reset( get_root_map( stake_delegations ) );
     326        3930 :   delta_pool_reset( get_delta_pool( stake_delegations ) );
     327        3930 :   fork_pool_ele_t * fork_pool = get_fork_pool( stake_delegations );
     328        3930 :   ulong max_forks = fork_pool_max( fork_pool );
     329       67110 :   for( ulong i=0UL; i<max_forks; i++ ) {
     330       63180 :     fork_map_reset( get_fork_map( stake_delegations, (ushort)i ) );
     331       63180 :   }
     332        3930 :   fork_pool_reset( fork_pool );
     333        3930 :   pubkey_pool_reset( get_pubkey_pool( stake_delegations ) );
     334        3930 :   pubkey_map_reset( get_pubkey_map( stake_delegations ) );
     335        3930 :   stake_delegations->effective_stake    = 0UL;
     336        3930 :   stake_delegations->activating_stake   = 0UL;
     337        3930 :   stake_delegations->deactivating_stake = 0UL;
     338        3930 :   stake_delegations->pool_idx_wmk_      = 0UL;
     339        3930 :   stake_delegations->pubkey_idx_wmk_    = 0UL;
     340        3930 :   stake_delegations->pubkey_fallback    = 0;
     341        3930 :   stake_delegations->fp_warmed_awarded  = 0;
     342        3930 :   fd_rwlock_unwrite( &stake_delegations->lock );
     343        3930 : }
     344             : 
     345             : fd_stake_delegation_t const *
     346             : fd_stake_delegation_root_query( fd_stake_delegations_t const * stake_delegations,
     347         213 :                                 fd_pubkey_t const *            stake_account ) {
     348         213 :   fd_stake_delegation_t * pool = get_root_pool( stake_delegations );
     349         213 :   root_map_t *            map = get_root_map( stake_delegations );
     350             : 
     351         213 :   return root_map_ele_query_const( map, stake_account, NULL, pool );
     352         213 : }
     353             : 
     354             : /* root_update is the unlocked core of fd_stake_delegations_root_update.
     355             :    Callers that already hold the lock use it directly.  Returns the root
     356             :    pool element holding the delegation, or NULL if the root pool was
     357             :    exhausted and the delegation got pushed into the pubkey fallback tier
     358             :    instead. */
     359             : 
     360             : static fd_stake_delegation_t *
     361             : root_update( fd_stake_delegations_t * stake_delegations,
     362             :              fd_pubkey_t const *      stake_account,
     363             :              fd_pubkey_t const *      vote_account,
     364             :              ulong                    stake,
     365             :              ulong                    activation_epoch,
     366             :              ulong                    deactivation_epoch,
     367             :              ulong                    credits_observed,
     368             :              ulong                    lamports,
     369             :              uint                     acc_dlen,
     370        4161 :              uchar                    warmup_cooldown_rate ) {
     371             :   /* On-chain delegation epochs are either the ULONG_MAX sentinel
     372             :      meaning bootstrap activation or not deactivating, or near the
     373             :      current epoch which is centuries below USHORT_MAX.  Don't allow
     374             :      exactly USHORT_MAX as raw input epoch, because that gets
     375             :      interpreted as the ULONG_MAX sentinel. */
     376        4161 :   FD_TEST( activation_epoch==ULONG_MAX   || activation_epoch<(ulong)USHORT_MAX   );
     377        4161 :   FD_TEST( deactivation_epoch==ULONG_MAX || deactivation_epoch<(ulong)USHORT_MAX );
     378             : 
     379        4161 :   fd_stake_delegation_t * pool = get_root_pool( stake_delegations );
     380        4161 :   root_map_t *            map  = get_root_map( stake_delegations );
     381             : 
     382        4161 :   fd_stake_delegation_t * stake_delegation = root_map_ele_query( map, stake_account, NULL, pool );
     383        4161 :   if( FD_LIKELY( !stake_delegation ) ) {
     384        4008 :     if( FD_UNLIKELY( !root_pool_free( pool ) ) ) {
     385           3 :       pubkey_fallback_enter( stake_delegations, stake_account );
     386           3 :       return NULL;
     387           3 :     }
     388        4005 :     stake_delegation                 = root_pool_ele_acquire( pool );
     389        4005 :     stake_delegation->stake_account  = *stake_account;
     390        4005 :     stake_delegations->pool_idx_wmk_ = fd_ulong_max( stake_delegations->pool_idx_wmk_, root_pool_idx( pool, stake_delegation )+1UL );
     391        4005 :     FD_CHECK_CRIT( root_map_ele_insert( map, stake_delegation, pool ), "unable to insert stake delegation into map" );
     392        4005 :     pubkey_ref_acquire( stake_delegations, stake_account );
     393        4005 :   }
     394             : 
     395        4158 :   stake_delegation->vote_account         = *vote_account;
     396        4158 :   stake_delegation->stake                = stake;
     397        4158 :   stake_delegation->lamports             = lamports;
     398        4158 :   stake_delegation->acc_dlen             = acc_dlen;
     399        4158 :   stake_delegation->activation_epoch     = (ushort)fd_ulong_min( activation_epoch, USHORT_MAX );
     400        4158 :   stake_delegation->deactivation_epoch   = (ushort)fd_ulong_min( deactivation_epoch, USHORT_MAX );
     401        4158 :   stake_delegation->credits_observed     = credits_observed;
     402        4158 :   stake_delegation->warmup_cooldown_rate = warmup_cooldown_rate;
     403        4158 :   stake_delegation->dne_in_root          = 0;
     404        4158 :   stake_delegation->delta_idx            = UINT_MAX;
     405        4158 :   stake_delegation->in_use               = 1;
     406        4158 :   stake_delegation->state                = FD_STAKE_DELEGATION_STATE_UNKNOWN;
     407             : 
     408        4158 :   return stake_delegation;
     409        4161 : }
     410             : 
     411             : void
     412             : fd_stake_delegations_root_update( fd_stake_delegations_t * stake_delegations,
     413             :                                   fd_pubkey_t const *      stake_account,
     414             :                                   fd_pubkey_t const *      vote_account,
     415             :                                   ulong                    stake,
     416             :                                   ulong                    activation_epoch,
     417             :                                   ulong                    deactivation_epoch,
     418             :                                   ulong                    credits_observed,
     419             :                                   ulong                    lamports,
     420             :                                   uint                     acc_dlen,
     421        3990 :                                   uchar                    warmup_cooldown_rate ) {
     422        3990 :   fd_rwlock_write( &stake_delegations->lock );
     423        3990 :   root_update( stake_delegations, stake_account, vote_account, stake, activation_epoch,
     424        3990 :                deactivation_epoch, credits_observed, lamports, acc_dlen, warmup_cooldown_rate );
     425        3990 :   fd_rwlock_unwrite( &stake_delegations->lock );
     426        3990 : }
     427             : 
     428             : #if FD_HAS_DOUBLE
     429             : 
     430             : void
     431             : fd_stake_delegations_refresh( fd_stake_delegations_t *   stake_delegations,
     432             :                               ulong                      epoch,
     433             :                               fd_stake_history_t const * stake_history,
     434             :                               ulong *                    warmup_cooldown_rate_epoch,
     435             :                               int                        use_fixed_point_stake_math,
     436             :                               fd_accdb_t *               accdb,
     437           0 :                               fd_accdb_fork_id_t         fork_id ) {
     438           0 :   fd_rwlock_write( &stake_delegations->lock );
     439             : 
     440           0 :   int history_contiguous = fd_sysvar_stake_history_is_contiguous( stake_history );
     441             : 
     442           0 :   stake_delegations->effective_stake    = 0UL;
     443           0 :   stake_delegations->activating_stake   = 0UL;
     444           0 :   stake_delegations->deactivating_stake = 0UL;
     445             : 
     446           0 :   root_map_t *                map      = get_root_map( stake_delegations );
     447           0 :   fd_stake_delegation_t *     pool     = get_root_pool( stake_delegations );
     448           0 :   pubkey_map_t *              ref_map  = get_pubkey_map( stake_delegations );
     449           0 :   fd_stake_delegation_ref_t * ref_pool = get_pubkey_pool( stake_delegations );
     450             : 
     451             :   /* Drive the refresh off the pubkey tier rather than the root pool.  The
     452             :      tier is a superset of the root by construction, so in the normal case
     453             :      this visits exactly the same accounts, and in fallback mode it also
     454             :      visits the accounts that never made it into the root map.  Refresh
     455             :      runs at boot before any fork exists, which is what makes it safe to
     456             :      rebuild both tiers in place and to reclaim entries whose refcounts
     457             :      fallback mode left meaningless. */
     458             : 
     459           0 :   ulong const wmk = stake_delegations->pubkey_idx_wmk_;
     460             : 
     461           0 : #define BATCH 64UL
     462           0 :   uchar const * pubkeys[ BATCH ];
     463           0 :   int           writable[ BATCH ];
     464           0 :   fd_acc_t      accs[ BATCH ];
     465           0 :   ulong         ref_idx[ BATCH ];
     466             : 
     467           0 :   ulong i = 0UL;
     468           0 :   while( i<wmk ) {
     469           0 :     ulong batch_n = 0UL;
     470           0 :     while( i<wmk && batch_n<BATCH ) {
     471           0 :       if( FD_LIKELY( ref_pool[ i ].refcnt ) ) {
     472           0 :         pubkeys[ batch_n ]  = ref_pool[ i ].stake_account.uc;
     473           0 :         writable[ batch_n ] = 0;
     474           0 :         ref_idx[ batch_n ]  = i;
     475           0 :         batch_n++;
     476           0 :       }
     477           0 :       i++;
     478           0 :     }
     479           0 :     if( FD_UNLIKELY( !batch_n ) ) continue;
     480             : 
     481           0 :     fd_accdb_acquire( accdb, fork_id, batch_n, pubkeys, writable, accs );
     482             : 
     483           0 :     for( ulong j=0UL; j<batch_n; j++ ) {
     484           0 :       fd_pubkey_t const *      stake_account = (fd_pubkey_t const *)pubkeys[ j ];
     485           0 :       fd_stake_state_t const * stake         = accs[ j ].lamports ? fd_stakes_get_state( &accs[ j ] ) : NULL;
     486             : 
     487           0 :       if( FD_UNLIKELY( !stake || stake->stake_type!=FD_STAKE_STATE_STAKE ) ) {
     488           0 :         fd_stake_delegation_t * delegation = root_map_ele_query( map, stake_account, NULL, pool );
     489           0 :         if( FD_LIKELY( delegation ) ) {
     490           0 :           root_map_idx_remove( map, stake_account, UINT_MAX, pool );
     491           0 :           delegation->in_use = 0;
     492           0 :           root_pool_ele_release( pool, delegation );
     493           0 :         }
     494           0 :         pubkey_map_ele_remove( ref_map, stake_account, NULL, ref_pool );
     495           0 :         ref_pool[ ref_idx[ j ] ].refcnt = 0U;
     496           0 :         pubkey_pool_ele_release( ref_pool, &ref_pool[ ref_idx[ j ] ] );
     497           0 :         continue;
     498           0 :       }
     499             : 
     500           0 :       fd_stake_delegation_t * delegation = root_update(
     501           0 :           stake_delegations,
     502           0 :           stake_account,
     503           0 :           &stake->stake.stake.delegation.voter_pubkey,
     504           0 :           stake->stake.stake.delegation.stake,
     505           0 :           stake->stake.stake.delegation.activation_epoch,
     506           0 :           stake->stake.stake.delegation.deactivation_epoch,
     507           0 :           stake->stake.stake.credits_observed,
     508           0 :           accs[ j ].lamports,
     509           0 :           (uint)accs[ j ].data_len,
     510           0 :           fd_stake_warmup_cooldown_rate( epoch, warmup_cooldown_rate_epoch ) );
     511             : 
     512           0 :       fd_stake_history_entry_t history = fd_delegation_activation_status( &stake->stake.stake.delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
     513           0 :       stake_delegations->effective_stake    += history.effective;
     514           0 :       stake_delegations->activating_stake   += history.activating;
     515           0 :       stake_delegations->deactivating_stake += history.deactivating;
     516             : 
     517             :       /* A delegation that the root pool couldn't take has no element to
     518             :          tag.  The fallback tier resolves it from the accounts database
     519             :          at the boundary and hands out an untagged copy. */
     520           0 :       if( FD_LIKELY( delegation ) ) {
     521           0 :         uchar state = fd_stake_delegation_classify( delegation, history, epoch );
     522           0 :         delegation->state = !history_contiguous ? FD_STAKE_DELEGATION_STATE_UNKNOWN : state;
     523           0 :         if( FD_LIKELY( delegation->state==FD_STAKE_DELEGATION_STATE_WARMED && !use_fixed_point_stake_math ) ) {
     524           0 :           stake_delegations->fp_warmed_awarded = 1;
     525           0 :         }
     526           0 :       }
     527           0 :     }
     528             : 
     529           0 :     fd_accdb_release( accdb, batch_n, accs );
     530           0 :   }
     531           0 : #undef BATCH
     532             : 
     533             :   /* Every surviving entry now holds exactly one reference, the root map's,
     534             :      because there are no fork deltas at boot.  Rewriting the refcounts
     535             :      repairs any that fallback mode left unpaired. */
     536           0 :   for( ulong idx=0UL; idx<stake_delegations->pubkey_idx_wmk_; idx++ ) {
     537           0 :     if( FD_LIKELY( ref_pool[ idx ].refcnt ) ) ref_pool[ idx ].refcnt = 1U;
     538           0 :   }
     539             : 
     540             :   /* If pruning freed enough room for every account to sit in the root map,
     541             :      the fallback is no longer needed.  This is the only place the sticky
     542             :      flag is cleared. */
     543           0 :   if( FD_UNLIKELY( stake_delegations->pubkey_fallback ) &&
     544           0 :       pubkey_pool_used( ref_pool )==root_pool_used( pool ) ) {
     545           0 :     FD_LOG_NOTICE(( "stake delegations no longer need the pubkey fallback; %lu stake accounts fit in the root map",
     546           0 :                     root_pool_used( pool ) ));
     547           0 :     stake_delegations->pubkey_fallback = 0;
     548           0 :   }
     549             : 
     550           0 :   fd_rwlock_unwrite( &stake_delegations->lock );
     551           0 : }
     552             : 
     553             : #endif
     554             : 
     555             : ulong
     556          90 : fd_stake_delegations_base_cnt( fd_stake_delegations_t const * stake_delegations ) {
     557          90 :   return root_pool_used( get_root_pool( stake_delegations ) );
     558          90 : }
     559             : 
     560             : ulong
     561          45 : fd_stake_delegations_pubkey_cnt( fd_stake_delegations_t const * stake_delegations ) {
     562          45 :   return pubkey_pool_used( get_pubkey_pool( stake_delegations ) );
     563          45 : }
     564             : 
     565             : /* Fork-aware delta operations */
     566             : 
     567             : ushort
     568        4746 : fd_stake_delegations_new_fork( fd_stake_delegations_t * stake_delegations ) {
     569        4746 :   fd_rwlock_write( &stake_delegations->lock );
     570        4746 :   fork_pool_ele_t * fork_pool = get_fork_pool( stake_delegations );
     571        4746 :   FD_CHECK_CRIT( fork_pool_free( fork_pool ), "no free forks in pool. The system has forked too wide." );
     572        4746 :   ushort fork_idx = (ushort)fork_pool_idx_acquire( fork_pool );
     573        4746 :   fd_rwlock_unwrite( &stake_delegations->lock );
     574             : 
     575        4746 :   return fork_idx;
     576        4746 : }
     577             : 
     578             : void
     579             : fd_stake_delegations_fork_update( fd_stake_delegations_t * stake_delegations,
     580             :                                   ushort                   fork_idx,
     581             :                                   fd_pubkey_t const *      stake_account,
     582             :                                   fd_pubkey_t const *      vote_account,
     583             :                                   ulong                    stake,
     584             :                                   ulong                    activation_epoch,
     585             :                                   ulong                    deactivation_epoch,
     586             :                                   ulong                    credits_observed,
     587             :                                   ulong                    lamports,
     588             :                                   uint                     acc_dlen,
     589         498 :                                   uchar                    warmup_cooldown_rate ) {
     590         498 :   fd_rwlock_write( &stake_delegations->lock );
     591             : 
     592             :   /* On-chain delegation epochs are either the ULONG_MAX sentinel
     593             :      meaning bootstrap activation or not deactivating, or near the
     594             :      current epoch which is centuries below USHORT_MAX.  Don't allow
     595             :      exactly USHORT_MAX as raw input epoch, because that gets
     596             :      interpreted as the ULONG_MAX sentinel. */
     597         498 :   FD_TEST( activation_epoch==ULONG_MAX   || activation_epoch<(ulong)USHORT_MAX   );
     598         498 :   FD_TEST( deactivation_epoch==ULONG_MAX || deactivation_epoch<(ulong)USHORT_MAX );
     599             : 
     600         498 :   fd_stake_delegation_t * delta_pool       = get_delta_pool( stake_delegations );
     601         498 :   fork_map_t *            map              = get_fork_map( stake_delegations, fork_idx );
     602         498 :   fd_stake_delegation_t * stake_delegation = fork_map_ele_query( map, stake_account, NULL, delta_pool );
     603         498 :   if( FD_LIKELY( !stake_delegation ) ) {
     604         444 :     if( FD_UNLIKELY( !delta_pool_free( delta_pool ) ) ) {
     605             :       /* The delta pool cannot take this stake account.  Record it in the
     606             :          pubkey fallback tier and drop the delegation state on the floor:
     607             :          the epoch boundary will read it back out of the accounts
     608             :          database. */
     609           3 :       pubkey_fallback_enter( stake_delegations, stake_account );
     610         441 :     } else {
     611         441 :       stake_delegation                = delta_pool_ele_acquire( delta_pool );
     612         441 :       stake_delegation->stake_account = *stake_account;
     613         441 :       fork_map_ele_insert( map, stake_delegation, delta_pool );
     614         441 :       pubkey_ref_acquire( stake_delegations, stake_account );
     615         441 :     }
     616         444 :   }
     617             : 
     618         498 :   if( FD_LIKELY( stake_delegation ) ) {
     619         495 :     stake_delegation->vote_account         = *vote_account;
     620         495 :     stake_delegation->stake                = stake;
     621         495 :     stake_delegation->lamports             = lamports;
     622         495 :     stake_delegation->acc_dlen             = acc_dlen;
     623         495 :     stake_delegation->activation_epoch     = (ushort)fd_ulong_min( activation_epoch, USHORT_MAX );
     624         495 :     stake_delegation->deactivation_epoch   = (ushort)fd_ulong_min( deactivation_epoch, USHORT_MAX );
     625         495 :     stake_delegation->credits_observed     = credits_observed;
     626         495 :     stake_delegation->warmup_cooldown_rate = warmup_cooldown_rate;
     627         495 :     stake_delegation->is_tombstone         = 0;
     628         495 :     stake_delegation->state                = FD_STAKE_DELEGATION_STATE_UNKNOWN;
     629         495 :   }
     630             : 
     631         498 :   fd_rwlock_unwrite( &stake_delegations->lock );
     632         498 : }
     633             : 
     634             : void
     635             : fd_stake_delegations_fork_remove( fd_stake_delegations_t * stake_delegations,
     636             :                                   ushort                   fork_idx,
     637          72 :                                   fd_pubkey_t const *      stake_account ) {
     638          72 :   fd_rwlock_write( &stake_delegations->lock );
     639             : 
     640          72 :   fd_stake_delegation_t * delta_pool       = get_delta_pool( stake_delegations );
     641          72 :   fork_map_t *            map              = get_fork_map( stake_delegations, fork_idx );
     642          72 :   fd_stake_delegation_t * stake_delegation = fork_map_ele_query( map, stake_account, NULL, delta_pool );
     643          72 :   if( FD_LIKELY( !stake_delegation ) ) {
     644          33 :     if( FD_UNLIKELY( !delta_pool_free( delta_pool ) ) ) {
     645           0 :       pubkey_fallback_enter( stake_delegations, stake_account );
     646          33 :     } else {
     647          33 :       stake_delegation                = delta_pool_ele_acquire( delta_pool );
     648          33 :       stake_delegation->stake_account = *stake_account;
     649          33 :       fork_map_ele_insert( map, stake_delegation, delta_pool );
     650          33 :       pubkey_ref_acquire( stake_delegations, stake_account );
     651          33 :     }
     652          33 :   }
     653             : 
     654          72 :   if( FD_LIKELY( stake_delegation ) ) {
     655          72 :     stake_delegation->lamports     = 0UL;
     656          72 :     stake_delegation->acc_dlen     = 0U;
     657          72 :     stake_delegation->is_tombstone = 1;
     658          72 :     stake_delegation->state        = FD_STAKE_DELEGATION_STATE_UNKNOWN;
     659             : 
     660          72 :     FD_BASE58_ENCODE_32_BYTES( stake_delegation->stake_account.uc, stake_account_out );
     661          72 :     FD_LOG_DEBUG(( "fork_remove: stake_account=%s", stake_account_out ));
     662          72 :   }
     663             : 
     664          72 :   fd_rwlock_unwrite( &stake_delegations->lock );
     665          72 : }
     666             : 
     667             : void
     668             : fd_stake_delegations_evict_fork( fd_stake_delegations_t * stake_delegations,
     669         717 :                                  ushort                   fork_idx ) {
     670         717 :   if( fork_idx==USHORT_MAX ) return;
     671             : 
     672         708 :   fd_rwlock_write( &stake_delegations->lock );
     673             : 
     674         708 :   fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
     675         708 :   fork_map_t *            fork_map   = get_fork_map( stake_delegations, fork_idx );
     676             : 
     677         708 :   fork_map_iter_t iter = fork_map_iter_init( fork_map, delta_pool );
     678        1044 :   while( !fork_map_iter_done( iter, fork_map, delta_pool ) ) {
     679         336 :     fd_stake_delegation_t * ele = fork_map_iter_ele( iter, fork_map, delta_pool );
     680         336 :     iter = fork_map_iter_next( iter, fork_map, delta_pool );
     681         336 :     pubkey_ref_release( stake_delegations, &ele->stake_account );
     682         336 :     delta_pool_ele_release( delta_pool, ele );
     683         336 :   }
     684         708 :   fork_map_reset( fork_map );
     685             : 
     686         708 :   fork_pool_idx_release( get_fork_pool( stake_delegations ), fork_idx );
     687             : 
     688         708 :   fd_rwlock_unwrite( &stake_delegations->lock );
     689         708 : }
     690             : 
     691             : void
     692             : fd_stake_delegations_apply_fork_delta( ulong                      epoch,
     693             :                                        fd_stake_history_t const * stake_history,
     694             :                                        ulong *                    warmup_cooldown_rate_epoch,
     695             :                                        int                        use_fixed_point_stake_math,
     696             :                                        fd_stake_delegations_t *   stake_delegations,
     697         534 :                                        ushort                     fork_idx ) {
     698         534 :   fd_rwlock_write( &stake_delegations->lock );
     699             : 
     700         534 :   int history_contiguous = fd_sysvar_stake_history_is_contiguous( stake_history );
     701             : 
     702         534 :   fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
     703         534 :   fork_map_t *            fork_map   = get_fork_map( stake_delegations, fork_idx );
     704             : 
     705         534 :   for( fork_map_iter_t iter = fork_map_iter_init( fork_map, delta_pool );
     706         714 :        !fork_map_iter_done( iter, fork_map, delta_pool );
     707         534 :        iter = fork_map_iter_next( iter, fork_map, delta_pool ) ) {
     708         180 :     fd_stake_delegation_t * stake_delegation = fork_map_iter_ele( iter, fork_map, delta_pool );
     709         180 :     if( FD_LIKELY( !stake_delegation->is_tombstone ) ) {
     710             :       /* If the acc in the delta is an update:
     711             :          - If the acc already exists, subtract the old version's stake
     712             :          - Insert/update the new version
     713             :          - Add the new version's stake to the totals */
     714         171 :       fd_stake_delegation_t const * old_delegation = fd_stake_delegation_root_query( stake_delegations, &stake_delegation->stake_account );
     715         171 :       if( FD_LIKELY( old_delegation ) ) {
     716         138 :         fd_stake_history_entry_t old_entry     = fd_stakes_activating_and_deactivating( old_delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
     717         138 :         stake_delegations->effective_stake    -= old_entry.effective;
     718         138 :         stake_delegations->activating_stake   -= old_entry.activating;
     719         138 :         stake_delegations->deactivating_stake -= old_entry.deactivating;
     720         138 :       }
     721             : 
     722         171 :       fd_stake_delegation_t * root_ele = root_update(
     723         171 :           stake_delegations,
     724         171 :           &stake_delegation->stake_account,
     725         171 :           &stake_delegation->vote_account,
     726         171 :           stake_delegation->stake,
     727         171 :           stake_delegation->activation_epoch==(ushort)USHORT_MAX   ? ULONG_MAX : stake_delegation->activation_epoch,
     728         171 :           stake_delegation->deactivation_epoch==(ushort)USHORT_MAX ? ULONG_MAX : stake_delegation->deactivation_epoch,
     729         171 :           stake_delegation->credits_observed,
     730         171 :           stake_delegation->lamports,
     731         171 :           stake_delegation->acc_dlen,
     732         171 :           stake_delegation->warmup_cooldown_rate );
     733             : 
     734         171 :       fd_stake_history_entry_t new_acc = fd_stakes_activating_and_deactivating( stake_delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
     735         171 :       stake_delegations->effective_stake    += new_acc.effective;
     736         171 :       stake_delegations->activating_stake   += new_acc.activating;
     737         171 :       stake_delegations->deactivating_stake += new_acc.deactivating;
     738             : 
     739             :       /* A delegation that the root pool couldn't take has no element to
     740             :          tag.  The fallback tier resolves it from the accounts database
     741             :          at the boundary and hands out an untagged copy. */
     742         171 :       if( FD_LIKELY( root_ele ) ) {
     743         171 :         uchar state = fd_stake_delegation_classify( root_ele, new_acc, epoch );
     744         171 :         root_ele->state = !history_contiguous ? FD_STAKE_DELEGATION_STATE_UNKNOWN : state;
     745         171 :         if( FD_LIKELY( root_ele->state==FD_STAKE_DELEGATION_STATE_WARMED && !use_fixed_point_stake_math ) ) {
     746         129 :           stake_delegations->fp_warmed_awarded = 1;
     747         129 :         }
     748         171 :       }
     749         171 :     } else {
     750             :       /* If the stake delegation in the delta is a tombstone, just
     751             :          remove the stake delegation from the root map and subtract
     752             :          its stake from the totals. */
     753           9 :       fd_stake_delegation_t * root_pool = get_root_pool( stake_delegations );
     754           9 :       root_map_t *            root_map  = get_root_map( stake_delegations );
     755           9 :       ulong delegation_idx = root_map_idx_query( root_map, &stake_delegation->stake_account, UINT_MAX, root_pool );
     756           9 :       if( FD_LIKELY( delegation_idx!=UINT_MAX ) ) {
     757           9 :         fd_stake_delegation_t * old_delegation = root_pool + delegation_idx;
     758           9 :         fd_stake_history_entry_t old_entry = fd_stakes_activating_and_deactivating( old_delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
     759           9 :         stake_delegations->effective_stake    -= old_entry.effective;
     760           9 :         stake_delegations->activating_stake   -= old_entry.activating;
     761           9 :         stake_delegations->deactivating_stake -= old_entry.deactivating;
     762           9 :         root_map_idx_remove( root_map, &stake_delegation->stake_account, delegation_idx, root_pool );
     763           9 :         old_delegation->in_use = 0;
     764           9 :         root_pool_idx_release( root_pool, delegation_idx );
     765           9 :         pubkey_ref_release( stake_delegations, &stake_delegation->stake_account );
     766           9 :       }
     767           9 :     }
     768         180 :   }
     769         534 :   FD_LOG_DEBUG(( "effective_stake=%lu, activating_stake=%lu, deactivating_stake=%lu", stake_delegations->effective_stake, stake_delegations->activating_stake, stake_delegations->deactivating_stake ));
     770             : 
     771         534 :   fd_rwlock_unwrite( &stake_delegations->lock );
     772         534 : }
     773             : 
     774             : void
     775           0 : fd_stake_delegations_iter_advance_fallback( fd_stake_delegations_iter_t * iter ) {
     776           0 :   fd_stake_delegations_t const * stake_delegations = iter->stake_delegations;
     777           0 :   fd_stake_delegation_ref_t *    pool              = get_pubkey_pool( stake_delegations );
     778             : 
     779           0 :   for(;;) {
     780           0 :     if( FD_LIKELY( iter->batch_idx<iter->batch_cnt ) ) {
     781           0 :       iter->ele = &iter->batch[ iter->batch_idx ];
     782           0 :       iter->idx = iter->batch_pool_idx[ iter->batch_idx ];
     783           0 :       return;
     784           0 :     }
     785             : 
     786           0 :     if( FD_UNLIKELY( iter->scan_idx>=iter->wmk ) ) {
     787           0 :       iter->ele = NULL;
     788           0 :       return;
     789           0 :     }
     790             : 
     791           0 :     uchar const * pubkeys [ FD_STAKE_DELEGATIONS_ITER_BATCH ];
     792           0 :     int           writable[ FD_STAKE_DELEGATIONS_ITER_BATCH ];
     793           0 :     ulong         pool_idx[ FD_STAKE_DELEGATIONS_ITER_BATCH ];
     794           0 :     fd_acc_t      accs    [ FD_STAKE_DELEGATIONS_ITER_BATCH ];
     795             : 
     796           0 :     ulong batch_n = 0UL;
     797           0 :     while( iter->scan_idx<iter->wmk && batch_n<FD_STAKE_DELEGATIONS_ITER_BATCH ) {
     798           0 :       fd_stake_delegation_ref_t * ref = pool + iter->scan_idx;
     799           0 :       if( FD_LIKELY( ref->refcnt ) ) {
     800           0 :         pubkeys [ batch_n ] = ref->stake_account.uc;
     801           0 :         writable[ batch_n ] = 0;
     802           0 :         pool_idx[ batch_n ] = iter->scan_idx;
     803           0 :         batch_n++;
     804           0 :       }
     805           0 :       iter->scan_idx++;
     806           0 :     }
     807           0 :     if( FD_UNLIKELY( !batch_n ) ) continue;
     808             : 
     809           0 :     fd_accdb_acquire( iter->accdb, iter->accdb_fork_id, batch_n, pubkeys, writable, accs );
     810             : 
     811           0 :     ulong out = 0UL;
     812           0 :     for( ulong j=0UL; j<batch_n; j++ ) {
     813           0 :       if( FD_UNLIKELY( !accs[ j ].lamports ) ) continue;
     814             : 
     815           0 :       fd_stake_state_t const * stake = fd_stakes_get_state( &accs[ j ] );
     816           0 :       if( FD_UNLIKELY( !stake || stake->stake_type!=FD_STAKE_STATE_STAKE ) ) continue;
     817             : 
     818           0 :       fd_delegation_t const * delegation = &stake->stake.stake.delegation;
     819           0 :       fd_stake_delegation_t * ele        = &iter->batch[ out ];
     820             : 
     821           0 :       ele->stake_account        = *(fd_pubkey_t const *)pubkeys[ j ];
     822           0 :       ele->vote_account         = delegation->voter_pubkey;
     823           0 :       ele->stake                = delegation->stake;
     824           0 :       ele->lamports             = accs[ j ].lamports;
     825           0 :       ele->credits_observed     = stake->stake.stake.credits_observed;
     826           0 :       ele->acc_dlen             = (uint)accs[ j ].data_len;
     827           0 :       ele->next_                = UINT_MAX;
     828           0 :       ele->delta_idx            = UINT_MAX;
     829           0 :       ele->activation_epoch     = (ushort)fd_ulong_min( delegation->activation_epoch, USHORT_MAX );
     830           0 :       ele->deactivation_epoch   = (ushort)fd_ulong_min( delegation->deactivation_epoch, USHORT_MAX );
     831           0 :       ele->is_tombstone         = 0;
     832           0 :       ele->warmup_cooldown_rate = fd_stake_warmup_cooldown_rate( iter->epoch, iter->warmup_cooldown_rate_epoch );
     833           0 :       ele->in_use               = 1;
     834           0 :       ele->state                = FD_STAKE_DELEGATION_STATE_UNKNOWN; /* Resolved copies are never tagged. */
     835             : 
     836           0 :       iter->batch_pool_idx[ out ] = pool_idx[ j ];
     837           0 :       out++;
     838           0 :     }
     839             : 
     840           0 :     fd_accdb_release( iter->accdb, batch_n, accs );
     841             : 
     842           0 :     iter->batch_cnt = out;
     843           0 :     iter->batch_idx = 0UL;
     844           0 :   }
     845           0 : }
     846             : 
     847             : fd_stake_delegations_iter_t *
     848             : fd_stake_delegations_iter_init( fd_stake_delegations_iter_t *  iter,
     849             :                                 fd_stake_delegations_t const * stake_delegations,
     850             :                                 fd_accdb_t *                   accdb,
     851             :                                 fd_accdb_fork_id_t             accdb_fork_id,
     852             :                                 ulong                          epoch,
     853        1296 :                                 ulong *                        warmup_cooldown_rate_epoch ) {
     854        1296 :   if( FD_UNLIKELY( !stake_delegations ) ) {
     855           0 :     FD_LOG_CRIT(( "NULL stake_delegations" ));
     856           0 :   }
     857             : 
     858        1296 :   iter->root_pool         = get_root_pool( stake_delegations );
     859        1296 :   iter->delta_pool        = get_delta_pool( stake_delegations );
     860        1296 :   iter->stake_delegations = stake_delegations;
     861        1296 :   iter->idx               = 0UL;
     862        1296 :   iter->scan_idx          = 0UL;
     863        1296 :   iter->batch_cnt         = 0UL;
     864        1296 :   iter->batch_idx         = 0UL;
     865        1296 :   iter->fallback          = stake_delegations->pubkey_fallback;
     866             : 
     867        1296 :   if( FD_UNLIKELY( iter->fallback ) ) {
     868           0 :     if( FD_UNLIKELY( !accdb ) ) {
     869           0 :       FD_LOG_CRIT(( "stake delegations are in pubkey fallback mode but no accounts database was "
     870           0 :                     "supplied to resolve them; iterating the root map alone would silently drop "
     871           0 :                     "stake accounts" ));
     872           0 :     }
     873           0 :     iter->accdb                      = accdb;
     874           0 :     iter->accdb_fork_id              = accdb_fork_id;
     875           0 :     iter->epoch                      = epoch;
     876           0 :     iter->warmup_cooldown_rate_epoch = warmup_cooldown_rate_epoch;
     877           0 :     iter->wmk                        = stake_delegations->pubkey_idx_wmk_;
     878           0 :     fd_stake_delegations_iter_advance_fallback( iter );
     879           0 :     return iter;
     880           0 :   }
     881             : 
     882        1296 :   iter->wmk = stake_delegations->pool_idx_wmk_;
     883        1296 :   fd_stake_delegations_iter_advance_private( iter );
     884             : 
     885        1296 :   return iter;
     886        1296 : }
     887             : 
     888             : void
     889             : fd_stake_delegations_mark_delta( fd_stake_delegations_t *   stake_delegations,
     890             :                                  ulong                      epoch,
     891             :                                  fd_stake_history_t const * stake_history,
     892             :                                  ulong *                    warmup_cooldown_rate_epoch,
     893             :                                  int                        use_fixed_point_stake_math,
     894         435 :                                  ushort                     fork_idx ) {
     895         435 :   root_map_t *            root_map   = get_root_map( stake_delegations );
     896         435 :   fd_stake_delegation_t * root_pool  = get_root_pool( stake_delegations );
     897         435 :   fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
     898         435 :   fork_map_t *            fork_map   = get_fork_map( stake_delegations, fork_idx );
     899             : 
     900         435 :   for( fork_map_iter_t iter = fork_map_iter_init( fork_map, delta_pool );
     901         600 :        !fork_map_iter_done( iter, fork_map, delta_pool );
     902         435 :        iter = fork_map_iter_next( iter, fork_map, delta_pool ) ) {
     903         165 :     fd_stake_delegation_t * delta_delegation = fork_map_iter_ele( iter, fork_map, delta_pool );
     904         165 :     fd_stake_delegation_t * base_delegation  = root_map_ele_query( root_map, &delta_delegation->stake_account, NULL, root_pool);
     905         165 :     if( FD_UNLIKELY( !base_delegation ) ) {
     906          69 :       if( FD_UNLIKELY( !root_pool_free( root_pool ) ) ) {
     907             :         /* No room to project this delta into the root for the duration of
     908             :            the iteration.  The stake totals are recomputed from scratch in
     909             :            fallback mode, so skipping the bookkeeping below is safe, and
     910             :            the boundary sweep picks the account up from the fallback
     911             :            tier. */
     912           0 :         pubkey_fallback_enter( stake_delegations, &delta_delegation->stake_account );
     913           0 :         continue;
     914           0 :       }
     915             :       /* No pubkey tier reference is taken for this projection, and
     916             :          correspondingly none is dropped when unmark_delta releases it: the
     917             :          delta entry driving this loop already holds one. */
     918          69 :       base_delegation                  = root_pool_ele_acquire( root_pool );
     919          69 :       base_delegation->stake_account   = delta_delegation->stake_account;
     920          69 :       base_delegation->lamports        = 0UL;
     921          69 :       base_delegation->acc_dlen        = 0U;
     922          69 :       base_delegation->dne_in_root     = 1;
     923          69 :       base_delegation->delta_idx       = (uint)delta_pool_idx( delta_pool, delta_delegation );
     924          69 :       base_delegation->in_use          = 1;
     925          69 :       base_delegation->state           = FD_STAKE_DELEGATION_STATE_UNKNOWN;
     926          69 :       stake_delegations->pool_idx_wmk_ = fd_ulong_max( stake_delegations->pool_idx_wmk_, root_pool_idx( root_pool, base_delegation )+1UL );
     927          69 :       root_map_ele_insert( root_map, base_delegation, root_pool );
     928          96 :     } else {
     929             :       /* Subtract the old version's stake if it's not a tombstone. */
     930          96 :       fd_stake_delegation_t *  old_delegation = base_delegation->delta_idx==UINT_MAX ? base_delegation : delta_pool_ele( delta_pool, base_delegation->delta_idx );
     931          96 :       if( FD_LIKELY( base_delegation->delta_idx==UINT_MAX || !old_delegation->is_tombstone ) ) {
     932          93 :         fd_stake_history_entry_t old_entry      = fd_stakes_activating_and_deactivating( old_delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
     933          93 :         stake_delegations->effective_stake    -= old_entry.effective;
     934          93 :         stake_delegations->activating_stake   -= old_entry.activating;
     935          93 :         stake_delegations->deactivating_stake -= old_entry.deactivating;
     936          93 :       }
     937             :       /* Update the base delegation to point to the new version. */
     938          96 :       base_delegation->delta_idx = (uint)delta_pool_idx( delta_pool, delta_delegation );
     939          96 :     }
     940             : 
     941             :     /* Add the new version's stake to the totals (as long as it's not a
     942             :        tombstone).*/
     943         165 :     if( FD_LIKELY( !delta_delegation->is_tombstone ) ) {
     944         138 :       fd_stake_history_entry_t new_acc = fd_stakes_activating_and_deactivating( delta_delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
     945         138 :       stake_delegations->effective_stake    += new_acc.effective;
     946         138 :       stake_delegations->activating_stake   += new_acc.activating;
     947         138 :       stake_delegations->deactivating_stake += new_acc.deactivating;
     948         138 :     }
     949         165 :   }
     950         435 : }
     951             : 
     952             : void
     953             : fd_stake_delegations_unmark_delta( fd_stake_delegations_t *   stake_delegations,
     954             :                                    ulong                      epoch,
     955             :                                    fd_stake_history_t const * stake_history,
     956             :                                    ulong *                    warmup_cooldown_rate_epoch,
     957             :                                    int                        use_fixed_point_stake_math,
     958         435 :                                    ushort                     fork_idx ) {
     959         435 :   root_map_t *            root_map   = get_root_map( stake_delegations );
     960         435 :   fd_stake_delegation_t * root_pool  = get_root_pool( stake_delegations );
     961         435 :   fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
     962         435 :   fork_map_t *            fork_map   = get_fork_map( stake_delegations, fork_idx );
     963             : 
     964         435 :   for( fork_map_iter_t iter = fork_map_iter_init( fork_map, delta_pool );
     965         600 :        !fork_map_iter_done( iter, fork_map, delta_pool );
     966         435 :        iter = fork_map_iter_next( iter, fork_map, delta_pool ) ) {
     967         165 :     fd_stake_delegation_t * delta_delegation = fork_map_iter_ele( iter, fork_map, delta_pool );
     968         165 :     fd_stake_delegation_t * base_delegation  = root_map_ele_query( root_map, &delta_delegation->stake_account, NULL, root_pool );
     969         165 :     if( FD_UNLIKELY( !base_delegation ) ) continue;
     970             : 
     971         165 :     uint delta_idx = (uint)delta_pool_idx( delta_pool, delta_delegation );
     972         165 :     if( FD_UNLIKELY( base_delegation->delta_idx!=delta_idx ) ) continue;
     973             : 
     974         147 :     if( FD_UNLIKELY( base_delegation->dne_in_root )) {
     975          69 :       if( FD_LIKELY( !delta_delegation->is_tombstone ) ) {
     976          66 :         fd_stake_history_entry_t acc = fd_stakes_activating_and_deactivating( delta_delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
     977          66 :         stake_delegations->effective_stake    -= acc.effective;
     978          66 :         stake_delegations->activating_stake   -= acc.activating;
     979          66 :         stake_delegations->deactivating_stake -= acc.deactivating;
     980          66 :       }
     981             : 
     982          69 :       base_delegation->dne_in_root = 0;
     983          69 :       base_delegation->delta_idx   = UINT_MAX;
     984          69 :       base_delegation->in_use      = 0;
     985          69 :       root_map_ele_remove( root_map, &delta_delegation->stake_account, NULL, root_pool );
     986          69 :       root_pool_ele_release( root_pool, base_delegation );
     987             : 
     988          78 :     } else {
     989          78 :       if( FD_LIKELY( !delta_delegation->is_tombstone ) ) {
     990          57 :         fd_stake_history_entry_t acc = fd_stakes_activating_and_deactivating( delta_delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
     991          57 :         stake_delegations->effective_stake    -= acc.effective;
     992          57 :         stake_delegations->activating_stake   -= acc.activating;
     993          57 :         stake_delegations->deactivating_stake -= acc.deactivating;
     994          57 :       }
     995             : 
     996          78 :       base_delegation->delta_idx = UINT_MAX;
     997             : 
     998          78 :       fd_stake_history_entry_t acc = fd_stakes_activating_and_deactivating( base_delegation, epoch, stake_history, warmup_cooldown_rate_epoch, use_fixed_point_stake_math );
     999          78 :       stake_delegations->effective_stake    += acc.effective;
    1000          78 :       stake_delegations->activating_stake   += acc.activating;
    1001          78 :       stake_delegations->deactivating_stake += acc.deactivating;
    1002          78 :     }
    1003         147 :   }
    1004         435 : }
    1005             : 
    1006             : void
    1007           6 : fd_stake_delegations_invalidate_warmed( fd_stake_delegations_t * stake_delegations ) {
    1008           6 :   fd_stake_delegation_t * root_pool = get_root_pool( stake_delegations );
    1009          12 :   for( ulong i=0UL; i<stake_delegations->pool_idx_wmk_; i++ ) {
    1010           6 :     fd_stake_delegation_t * delegation = &root_pool[ i ];
    1011           6 :     if( FD_LIKELY( delegation->in_use && delegation->state==FD_STAKE_DELEGATION_STATE_WARMED ) ) {
    1012           6 :       delegation->state = FD_STAKE_DELEGATION_STATE_UNKNOWN;
    1013           6 :     }
    1014           6 :   }
    1015           6 :   stake_delegations->fp_warmed_awarded = 0;
    1016           6 : }

Generated by: LCOV version 1.14