LCOV - code coverage report
Current view: top level - flamenco/stakes - fd_stake_delegations.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 631 749 84.2 %
Date: 2026-09-17 04:28:31 Functions: 34 35 97.1 %

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

Generated by: LCOV version 1.14