LCOV - code coverage report
Current view: top level - flamenco/stakes - fd_stake_delegations.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 340 415 81.9 %
Date: 2026-03-31 06:22:16 Functions: 27 28 96.4 %

          Line data    Source code
       1             : #include "fd_stake_delegations.h"
       2             : #include "../accdb/fd_accdb_pipe.h"
       3             : #include "fd_stakes.h"
       4             : 
       5             : #define POOL_NAME  root_pool
       6         792 : #define POOL_T     fd_stake_delegation_t
       7      799050 : #define POOL_NEXT  next_
       8             : #define POOL_IDX_T uint
       9             : #include "../../util/tmpl/fd_pool.c"
      10             : 
      11             : #define MAP_NAME               root_map
      12             : #define MAP_KEY_T              fd_pubkey_t
      13             : #define MAP_ELE_T              fd_stake_delegation_t
      14         105 : #define MAP_KEY                stake_account
      15         402 : #define MAP_KEY_EQ(k0,k1)      (fd_pubkey_eq( k0, k1 ))
      16         591 : #define MAP_KEY_HASH(key,seed) (fd_funk_rec_key_hash1( key->uc, seed ))
      17         468 : #define MAP_NEXT               next_
      18        1788 : #define MAP_IDX_T              uint
      19             : #include "../../util/tmpl/fd_map_chain.c"
      20             : 
      21             : #define POOL_NAME  delta_pool
      22         792 : #define POOL_T     fd_stake_delegation_t
      23      799107 : #define POOL_NEXT  next_
      24             : #define POOL_IDX_T uint
      25             : #include "../../util/tmpl/fd_pool.c"
      26             : 
      27             : #define DLIST_NAME  fork_dlist
      28             : #define DLIST_ELE_T fd_stake_delegation_t
      29         132 : #define DLIST_PREV  prev_
      30         621 : #define DLIST_NEXT  next_
      31             : #define DLIST_IDX_T uint
      32             : #include "../../util/tmpl/fd_dlist.c"
      33             : 
      34             : struct fork_pool_ele { ushort next; };
      35             : typedef struct fork_pool_ele fork_pool_ele_t;
      36             : 
      37             : #define POOL_NAME  fork_pool
      38         792 : #define POOL_T     fork_pool_ele_t
      39             : #define POOL_IDX_T ushort
      40             : #include "../../util/tmpl/fd_pool.c"
      41             : 
      42             : /* Internal getters for base map + pool */
      43             : 
      44             : static inline fd_stake_delegation_t *
      45         669 : get_root_pool( fd_stake_delegations_t const * stake_delegations ) {
      46         669 :   return fd_type_pun( (uchar *)stake_delegations + stake_delegations->pool_offset_ );
      47         669 : }
      48             : 
      49             : static inline root_map_t *
      50         600 : get_root_map( fd_stake_delegations_t const * stake_delegations ) {
      51         600 :   return fd_type_pun( (uchar *)stake_delegations + stake_delegations->map_offset_ );
      52         600 : }
      53             : 
      54             : /* Internal getters for delta pool + fork structures */
      55             : 
      56             : static inline fd_stake_delegation_t *
      57         909 : get_delta_pool( fd_stake_delegations_t const * stake_delegations ) {
      58         909 :   return fd_type_pun( (uchar *)stake_delegations + stake_delegations->delta_pool_offset_ );
      59         909 : }
      60             : 
      61             : static inline fork_pool_ele_t *
      62         894 : get_fork_pool( fd_stake_delegations_t const * stake_delegations ) {
      63         894 :   return fd_type_pun( (uchar *)stake_delegations + stake_delegations->fork_pool_offset_ );
      64         894 : }
      65             : 
      66             : static inline fork_dlist_t *
      67             : get_fork_dlist( fd_stake_delegations_t const * stake_delegations,
      68         687 :                 ushort                         fork_idx ) {
      69         687 :   return fd_type_pun( (uchar *)stake_delegations + stake_delegations->dlist_offsets_[ fork_idx ] );
      70         687 : }
      71             : 
      72             : ulong
      73       12192 : fd_stake_delegations_align( void ) {
      74       12192 :   return FD_STAKE_DELEGATIONS_ALIGN;
      75       12192 : }
      76             : 
      77             : ulong
      78             : fd_stake_delegations_footprint( ulong max_stake_accounts,
      79             :                                 ulong expected_stake_accounts,
      80        1965 :                                 ulong max_live_slots ) {
      81             : 
      82        1965 :   ulong map_chain_cnt = root_map_chain_cnt_est( expected_stake_accounts );
      83             : 
      84        1965 :   ulong l = FD_LAYOUT_INIT;
      85        1965 :   l = FD_LAYOUT_APPEND( l, fd_stake_delegations_align(), sizeof(fd_stake_delegations_t) );
      86        1965 :   l = FD_LAYOUT_APPEND( l, root_pool_align(),            root_pool_footprint( max_stake_accounts ) );
      87        1965 :   l = FD_LAYOUT_APPEND( l, root_map_align(),             root_map_footprint( map_chain_cnt ) );
      88        1965 :   l = FD_LAYOUT_APPEND( l, delta_pool_align(),           delta_pool_footprint( max_stake_accounts ) );
      89        1965 :   l = FD_LAYOUT_APPEND( l, fork_pool_align(),            fork_pool_footprint( max_live_slots ) );
      90        5901 :   for( ulong i=0UL; i<max_live_slots; i++ ) {
      91        3936 :     l = FD_LAYOUT_APPEND( l, fork_dlist_align(), fork_dlist_footprint() );
      92        3936 :   }
      93             : 
      94        1965 :   return FD_LAYOUT_FINI( l, fd_stake_delegations_align() );
      95        1965 : }
      96             : 
      97             : void *
      98             : fd_stake_delegations_new( void * mem,
      99             :                           ulong  seed,
     100             :                           ulong  max_stake_accounts,
     101             :                           ulong  expected_stake_accounts,
     102         402 :                           ulong  max_live_slots ) {
     103         402 :   if( FD_UNLIKELY( !mem ) ) {
     104           3 :     FD_LOG_WARNING(( "NULL mem" ));
     105           3 :     return NULL;
     106           3 :   }
     107             : 
     108         399 :   if( FD_UNLIKELY( !max_stake_accounts ) ) {
     109           3 :     FD_LOG_WARNING(( "max_stake_accounts is 0" ));
     110           3 :     return NULL;
     111           3 :   }
     112             : 
     113         396 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_stake_delegations_align() ) ) ) {
     114           0 :     FD_LOG_WARNING(( "misaligned mem" ));
     115           0 :     return NULL;
     116           0 :   }
     117             : 
     118         396 :   if( FD_UNLIKELY( max_live_slots>FD_STAKE_DELEGATIONS_FORK_MAX ) ) {
     119           0 :     FD_LOG_WARNING(( "max_live_slots is too large" ));
     120           0 :     return NULL;
     121           0 :   }
     122             : 
     123         396 :   ulong map_chain_cnt = root_map_chain_cnt_est( expected_stake_accounts );
     124             : 
     125         396 :   FD_SCRATCH_ALLOC_INIT( l, mem );
     126         396 :   fd_stake_delegations_t * stake_delegations = FD_SCRATCH_ALLOC_APPEND( l, fd_stake_delegations_align(), sizeof(fd_stake_delegations_t) );
     127         396 :   void *                   pool_mem          = FD_SCRATCH_ALLOC_APPEND( l, root_pool_align(),            root_pool_footprint( max_stake_accounts ) );
     128         396 :   void *                   map_mem           = FD_SCRATCH_ALLOC_APPEND( l, root_map_align(),             root_map_footprint( map_chain_cnt ) );
     129         396 :   void *                   delta_pool_mem    = FD_SCRATCH_ALLOC_APPEND( l, delta_pool_align(),           delta_pool_footprint( max_stake_accounts ) );
     130         396 :   void *                   fork_pool_mem     = FD_SCRATCH_ALLOC_APPEND( l, fork_pool_align(),            fork_pool_footprint( max_live_slots ) );
     131        1260 :   for( ushort i=0; i<(ushort)max_live_slots; i++ ) {
     132         864 :     void * fork_dlist_mem = FD_SCRATCH_ALLOC_APPEND( l, fork_dlist_align(), fork_dlist_footprint() );
     133           0 :     fork_dlist_t * dlist = fork_dlist_join( fork_dlist_new( fork_dlist_mem ) );
     134         864 :     if( FD_UNLIKELY( !dlist ) ) {
     135           0 :       FD_LOG_WARNING(( "Failed to create fork dlist" ));
     136           0 :       return NULL;
     137           0 :     }
     138         864 :     stake_delegations->dlist_offsets_[ i ] = (ulong)dlist - (ulong)mem;
     139         864 :   }
     140             : 
     141         396 :   if( FD_UNLIKELY( FD_SCRATCH_ALLOC_FINI( l, fd_stake_delegations_align() )!=(ulong)mem+fd_stake_delegations_footprint( max_stake_accounts, expected_stake_accounts, max_live_slots ) ) ) {
     142           0 :     FD_LOG_WARNING(( "fd_stake_delegations_new: bad layout" ));
     143           0 :     return NULL;
     144           0 :   }
     145             : 
     146         396 :   fd_stake_delegation_t * root_pool = root_pool_join( root_pool_new( pool_mem, max_stake_accounts ) );
     147         396 :   if( FD_UNLIKELY( !root_pool ) ) {
     148           0 :     FD_LOG_WARNING(( "Failed to create stake delegations pool" ));
     149           0 :     return NULL;
     150           0 :   }
     151             : 
     152         396 :   root_map_t * root_map = root_map_join( root_map_new( map_mem, map_chain_cnt, seed ) );
     153         396 :   if( FD_UNLIKELY( !root_map ) ) {
     154           0 :     FD_LOG_WARNING(( "Failed to create stake delegations map" ));
     155           0 :     return NULL;
     156           0 :   }
     157             : 
     158         396 :   fd_stake_delegation_t * delta_pool = delta_pool_join( delta_pool_new( delta_pool_mem, max_stake_accounts ) );
     159         396 :   if( FD_UNLIKELY( !delta_pool ) ) {
     160           0 :     FD_LOG_WARNING(( "Failed to create stake delegation delta pool" ));
     161           0 :     return NULL;
     162           0 :   }
     163             : 
     164         396 :   fork_pool_ele_t * fork_pool = fork_pool_join( fork_pool_new( fork_pool_mem, max_live_slots ) );
     165         396 :   if( FD_UNLIKELY( !fork_pool ) ) {
     166           0 :     FD_LOG_WARNING(( "Failed to create fork pool" ));
     167           0 :     return NULL;
     168           0 :   }
     169             : 
     170         396 :   stake_delegations->max_stake_accounts_      = max_stake_accounts;
     171         396 :   stake_delegations->expected_stake_accounts_ = expected_stake_accounts;
     172         396 :   stake_delegations->pool_offset_             = (ulong)root_pool - (ulong)mem;
     173         396 :   stake_delegations->map_offset_              = (ulong)root_map - (ulong)mem;
     174         396 :   stake_delegations->delta_pool_offset_       = (ulong)delta_pool - (ulong)mem;
     175         396 :   stake_delegations->fork_pool_offset_        = (ulong)fork_pool - (ulong)mem;
     176             : 
     177         396 :   stake_delegations->effective_stake    = 0UL;
     178         396 :   stake_delegations->activating_stake   = 0UL;
     179         396 :   stake_delegations->deactivating_stake = 0UL;
     180             : 
     181         396 :   fd_rwlock_new( &stake_delegations->delta_lock );
     182             : 
     183         396 :   FD_COMPILER_MFENCE();
     184         396 :   FD_VOLATILE( stake_delegations->magic ) = FD_STAKE_DELEGATIONS_MAGIC;
     185         396 :   FD_COMPILER_MFENCE();
     186             : 
     187         396 :   return mem;
     188         396 : }
     189             : 
     190             : fd_stake_delegations_t *
     191         402 : fd_stake_delegations_join( void * mem ) {
     192         402 :   if( FD_UNLIKELY( !mem ) ) {
     193           3 :     FD_LOG_WARNING(( "NULL mem" ));
     194           3 :     return NULL;
     195           3 :   }
     196             : 
     197         399 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_stake_delegations_align() ) ) ) {
     198           0 :     FD_LOG_WARNING(( "misaligned mem" ));
     199           0 :     return NULL;
     200           0 :   }
     201             : 
     202         399 :   fd_stake_delegations_t * stake_delegations = (fd_stake_delegations_t *)mem;
     203             : 
     204         399 :   if( FD_UNLIKELY( stake_delegations->magic!=FD_STAKE_DELEGATIONS_MAGIC ) ) {
     205           3 :     FD_LOG_WARNING(( "Invalid stake delegations magic" ));
     206           3 :     return NULL;
     207           3 :   }
     208             : 
     209         396 :   return stake_delegations;
     210         399 : }
     211             : 
     212             : void
     213           3 : fd_stake_delegations_init( fd_stake_delegations_t * stake_delegations ) {
     214           3 :   root_map_t *            map  = get_root_map( stake_delegations );
     215           3 :   fd_stake_delegation_t * pool = get_root_pool( stake_delegations );
     216           3 :   root_pool_reset( pool );
     217           3 :   root_map_reset( map );
     218           3 : }
     219             : 
     220             : fd_stake_delegation_t const *
     221             : fd_stake_delegation_root_query( fd_stake_delegations_t const * stake_delegations,
     222          30 :                                 fd_pubkey_t const *            stake_account ) {
     223          30 :   fd_stake_delegation_t * pool = get_root_pool( stake_delegations );
     224          30 :   root_map_t *            map = get_root_map( stake_delegations );
     225             : 
     226          30 :   return root_map_ele_query_const( map, stake_account, NULL, pool );
     227          30 : }
     228             : 
     229             : void
     230             : fd_stake_delegations_root_update( fd_stake_delegations_t * stake_delegations,
     231             :                                   fd_pubkey_t const *      stake_account,
     232             :                                   fd_pubkey_t const *      vote_account,
     233             :                                   ulong                    stake,
     234             :                                   ulong                    activation_epoch,
     235             :                                   ulong                    deactivation_epoch,
     236             :                                   ulong                    credits_observed,
     237          54 :                                   double                   warmup_cooldown_rate ) {
     238          54 :   fd_stake_delegation_t * pool = get_root_pool( stake_delegations );
     239          54 :   root_map_t *            map = get_root_map( stake_delegations );
     240             : 
     241          54 :   fd_stake_delegation_t * stake_delegation = root_map_ele_query( map, stake_account, NULL, pool );
     242          54 :   if( !stake_delegation ) {
     243          39 :     FD_CRIT( root_pool_free( pool ), "no free stake delegations in pool" );
     244          39 :     stake_delegation = root_pool_ele_acquire( pool );
     245          39 :     stake_delegation->stake_account = *stake_account;
     246          39 :     FD_CRIT( root_map_ele_insert( map, stake_delegation, pool ), "unable to insert stake delegation into map" );
     247          39 :   }
     248             : 
     249          54 :   stake_delegation->vote_account         = *vote_account;
     250          54 :   stake_delegation->stake                = stake;
     251          54 :   stake_delegation->activation_epoch     = (ushort)fd_ulong_min( activation_epoch, USHORT_MAX );
     252          54 :   stake_delegation->deactivation_epoch   = (ushort)fd_ulong_min( deactivation_epoch, USHORT_MAX );
     253          54 :   stake_delegation->credits_observed     = credits_observed;
     254          54 :   stake_delegation->warmup_cooldown_rate = fd_stake_delegations_warmup_cooldown_rate_enum( warmup_cooldown_rate );
     255          54 :   stake_delegation->dne_in_root          = 0;
     256          54 :   stake_delegation->delta_idx            = UINT_MAX;
     257          54 : }
     258             : 
     259             : static inline void
     260             : fd_stake_delegations_remove( fd_stake_delegations_t * stake_delegations,
     261           3 :                              fd_pubkey_t const *      stake_account ) {
     262           3 :   fd_stake_delegation_t * pool = get_root_pool( stake_delegations );
     263           3 :   root_map_t *            map  = get_root_map( stake_delegations );
     264             : 
     265           3 :   ulong delegation_idx = root_map_idx_query( map, stake_account, UINT_MAX, pool );
     266           3 :   if( FD_UNLIKELY( delegation_idx==UINT_MAX ) ) return;
     267             : 
     268           3 :   root_map_idx_remove( map, stake_account, delegation_idx, pool );
     269           3 :   root_pool_idx_release( pool, delegation_idx );
     270           3 : }
     271             : 
     272             : #if FD_HAS_DOUBLE
     273             : 
     274             : void
     275             : fd_stake_delegations_refresh( fd_stake_delegations_t *   stake_delegations,
     276             :                               ulong                      epoch,
     277             :                               fd_stake_history_t const * stake_history,
     278             :                               ulong *                    warmup_cooldown_rate_epoch,
     279             :                               fd_accdb_user_t *          accdb,
     280           0 :                               fd_funk_txn_xid_t const *  xid ) {
     281             : 
     282           0 :   stake_delegations->effective_stake    = 0UL;
     283           0 :   stake_delegations->activating_stake   = 0UL;
     284           0 :   stake_delegations->deactivating_stake = 0UL;
     285             : 
     286           0 :   root_map_t *            map  = get_root_map( stake_delegations );
     287           0 :   fd_stake_delegation_t * pool = get_root_pool( stake_delegations );
     288             : 
     289           0 :   fd_accdb_ro_pipe_t ro_pipe[1];
     290           0 :   fd_accdb_ro_pipe_init( ro_pipe, accdb, xid );
     291           0 :   ulong const job_cnt = fd_stake_delegations_cnt( stake_delegations );
     292           0 :   for( ulong i=0UL; i<job_cnt; i++ ) {
     293             : 
     294             :     /* stream out read requests */
     295           0 :     fd_accdb_ro_pipe_enqueue( ro_pipe, &pool[ i ].stake_account );
     296           0 :     if( FD_UNLIKELY( i+1UL==job_cnt ) ) {
     297           0 :       fd_accdb_ro_pipe_flush( ro_pipe );
     298           0 :     }
     299             : 
     300             :     /* handle a batch of completions */
     301           0 :     fd_accdb_ro_t * ro;
     302           0 :     while( (ro = fd_accdb_ro_pipe_poll( ro_pipe )) ) {
     303           0 :       fd_pubkey_t const * address = fd_accdb_ref_address( ro );
     304           0 :       fd_stake_delegation_t * delegation = root_map_ele_query( map, address, NULL, pool );
     305           0 :       if( FD_UNLIKELY( !delegation ) ) continue;
     306             : 
     307           0 :       fd_stake_state_t const * stake = fd_stakes_get_state( ro->meta );
     308           0 :       if( FD_UNLIKELY( !stake ) ) goto remove;
     309           0 :       if( FD_UNLIKELY( stake->stake_type != FD_STAKE_STATE_STAKE ) ) goto remove;
     310             : 
     311           0 :       fd_stake_delegations_root_update(
     312           0 :           stake_delegations,
     313           0 :           address,
     314           0 :           &stake->stake.stake.delegation.voter_pubkey,
     315           0 :           stake->stake.stake.delegation.stake,
     316           0 :           stake->stake.stake.delegation.activation_epoch,
     317           0 :           stake->stake.stake.delegation.deactivation_epoch,
     318           0 :           stake->stake.stake.credits_observed,
     319           0 :           stake->stake.stake.delegation.warmup_cooldown_rate );
     320             : 
     321           0 :       fd_stake_history_entry_t entry = stake_activating_and_deactivating( &stake->stake.stake.delegation, epoch, stake_history, warmup_cooldown_rate_epoch );
     322           0 :       stake_delegations->effective_stake    += entry.effective;
     323           0 :       stake_delegations->activating_stake   += entry.activating;
     324           0 :       stake_delegations->deactivating_stake += entry.deactivating;
     325           0 :       continue; /* ok */
     326             : 
     327           0 :     remove:
     328           0 :       root_map_idx_remove( map, address, UINT_MAX, pool );
     329           0 :       root_pool_ele_release( pool, delegation );
     330           0 :     }
     331           0 :   }
     332           0 :   fd_accdb_ro_pipe_fini( ro_pipe );
     333           0 : }
     334             : 
     335             : #endif
     336             : 
     337             : ulong
     338          69 : fd_stake_delegations_cnt( fd_stake_delegations_t const * stake_delegations ) {
     339          69 :   return root_pool_used( get_root_pool( stake_delegations ) );
     340          69 : }
     341             : 
     342             : /* Fork-aware delta operations */
     343             : 
     344             : ushort
     345         699 : fd_stake_delegations_new_fork( fd_stake_delegations_t * stake_delegations ) {
     346         699 :   fork_pool_ele_t * fork_pool = get_fork_pool( stake_delegations );
     347         699 :   FD_CRIT( fork_pool_free( fork_pool ), "no free forks in pool. The system has forked too wide." );
     348         699 :   ushort fork_idx = (ushort)fork_pool_idx_acquire( fork_pool );
     349             : 
     350         699 :   return fork_idx;
     351         699 : }
     352             : 
     353             : void
     354             : fd_stake_delegations_fork_update( fd_stake_delegations_t * stake_delegations,
     355             :                                   ushort                   fork_idx,
     356             :                                   fd_pubkey_t const *      stake_account,
     357             :                                   fd_pubkey_t const *      vote_account,
     358             :                                   ulong                    stake,
     359             :                                   ulong                    activation_epoch,
     360             :                                   ulong                    deactivation_epoch,
     361             :                                   ulong                    credits_observed,
     362         105 :                                   double                   warmup_cooldown_rate ) {
     363         105 :   fd_rwlock_write( &stake_delegations->delta_lock );
     364             : 
     365         105 :   fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
     366         105 :   FD_CRIT( delta_pool_free( delta_pool ), "no free stake delegations in pool" );
     367             : 
     368         105 :   fork_dlist_t * dlist = get_fork_dlist( stake_delegations, fork_idx );
     369             : 
     370         105 :   fd_stake_delegation_t * stake_delegation = delta_pool_ele_acquire( delta_pool );
     371             : 
     372         105 :   fork_dlist_ele_push_tail( dlist, stake_delegation, delta_pool );
     373             : 
     374         105 :   stake_delegation->stake_account        = *stake_account;
     375         105 :   stake_delegation->vote_account         = *vote_account;
     376         105 :   stake_delegation->stake                = stake;
     377         105 :   stake_delegation->activation_epoch     = (ushort)fd_ulong_min( activation_epoch, USHORT_MAX );
     378         105 :   stake_delegation->deactivation_epoch   = (ushort)fd_ulong_min( deactivation_epoch, USHORT_MAX );
     379         105 :   stake_delegation->credits_observed     = credits_observed;
     380         105 :   stake_delegation->warmup_cooldown_rate = fd_stake_delegations_warmup_cooldown_rate_enum( warmup_cooldown_rate );
     381         105 :   stake_delegation->is_tombstone         = 0;
     382             : 
     383         105 :   fd_rwlock_unwrite( &stake_delegations->delta_lock );
     384         105 : }
     385             : 
     386             : void
     387             : fd_stake_delegations_fork_remove( fd_stake_delegations_t * stake_delegations,
     388             :                                   ushort                   fork_idx,
     389          27 :                                   fd_pubkey_t const *      stake_account ) {
     390          27 :   fd_rwlock_write( &stake_delegations->delta_lock );
     391             : 
     392          27 :   fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
     393          27 :   FD_CRIT( delta_pool_free( delta_pool ), "no free stake delegations in pool" );
     394             : 
     395          27 :   fd_stake_delegation_t * stake_delegation = delta_pool_ele_acquire( delta_pool );
     396             : 
     397          27 :   fork_dlist_t * dlist = get_fork_dlist( stake_delegations, fork_idx );
     398          27 :   fork_dlist_ele_push_tail( dlist, stake_delegation, delta_pool );
     399             : 
     400          27 :   stake_delegation->stake_account = *stake_account;
     401          27 :   stake_delegation->is_tombstone  = 1;
     402             : 
     403          27 :   fd_rwlock_unwrite( &stake_delegations->delta_lock );
     404          27 : }
     405             : 
     406             : void
     407             : fd_stake_delegations_evict_fork( fd_stake_delegations_t * stake_delegations,
     408         201 :                                  ushort                   fork_idx ) {
     409         201 :   if( fork_idx==USHORT_MAX ) return;
     410             : 
     411         195 :   fd_rwlock_write( &stake_delegations->delta_lock );
     412             : 
     413         195 :   fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
     414             : 
     415         195 :   fork_dlist_t * dlist = get_fork_dlist( stake_delegations, fork_idx );
     416         324 :   while( !fork_dlist_is_empty( dlist, delta_pool ) ) {
     417         129 :     fd_stake_delegation_t * ele = fork_dlist_ele_pop_head( dlist, delta_pool );
     418         129 :     delta_pool_ele_release( delta_pool, ele );
     419         129 :   }
     420             : 
     421         195 :   fork_pool_idx_release( get_fork_pool( stake_delegations ), fork_idx );
     422             : 
     423         195 :   fd_rwlock_unwrite( &stake_delegations->delta_lock );
     424         195 : }
     425             : 
     426             : void
     427             : fd_stake_delegations_apply_fork_delta( ulong                      epoch,
     428             :                                        fd_stake_history_t const * stake_history,
     429             :                                        ulong *                    warmup_cooldown_rate_epoch,
     430             :                                        fd_stake_delegations_t *   stake_delegations,
     431          72 :                                        ushort                     fork_idx ) {
     432             : 
     433          72 :   fork_dlist_t *          dlist      = get_fork_dlist( stake_delegations, fork_idx );
     434          72 :   fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
     435             : 
     436          72 :   for( fork_dlist_iter_t iter = fork_dlist_iter_fwd_init( dlist, delta_pool );
     437         102 :        !fork_dlist_iter_done( iter, dlist, delta_pool );
     438          72 :        iter = fork_dlist_iter_fwd_next( iter, dlist, delta_pool ) ) {
     439          30 :     fd_stake_delegation_t * stake_delegation = fork_dlist_iter_ele( iter, dlist, delta_pool );
     440          30 :     if( FD_LIKELY( !stake_delegation->is_tombstone ) ) {
     441             :       /* If the entry in the delta is an update:
     442             :          - If the entry already exists, subtract the old version's stake
     443             :          - Insert/update the new version
     444             :          - Add the new version's stake to the totals */
     445          27 :       fd_stake_delegation_t const * old_delegation = fd_stake_delegation_root_query( stake_delegations, &stake_delegation->stake_account );
     446          27 :       if( FD_LIKELY( old_delegation ) ) {
     447          12 :         fd_stake_history_entry_t old_entry = fd_stakes_activating_and_deactivating( old_delegation, epoch, stake_history, warmup_cooldown_rate_epoch );
     448          12 :         stake_delegations->effective_stake    -= old_entry.effective;
     449          12 :         stake_delegations->activating_stake   -= old_entry.activating;
     450          12 :         stake_delegations->deactivating_stake -= old_entry.deactivating;
     451          12 :       }
     452             : 
     453          27 :       fd_stake_delegations_root_update(
     454          27 :           stake_delegations,
     455          27 :           &stake_delegation->stake_account,
     456          27 :           &stake_delegation->vote_account,
     457          27 :           stake_delegation->stake,
     458          27 :           stake_delegation->activation_epoch,
     459          27 :           stake_delegation->deactivation_epoch,
     460          27 :           stake_delegation->credits_observed,
     461          27 :           fd_stake_delegations_warmup_cooldown_rate_to_double( stake_delegation->warmup_cooldown_rate ) );
     462             : 
     463          27 :       fd_stake_history_entry_t new_entry = fd_stakes_activating_and_deactivating( stake_delegation, epoch, stake_history, warmup_cooldown_rate_epoch );
     464          27 :       stake_delegations->effective_stake    += new_entry.effective;
     465          27 :       stake_delegations->activating_stake   += new_entry.activating;
     466          27 :       stake_delegations->deactivating_stake += new_entry.deactivating;
     467          27 :     } else {
     468             :       /* If the stake delegation in the delta is a tombstone, just
     469             :          remove the stake delegation from the root map and subtract
     470             :          it's stake from the totals. */
     471           3 :       fd_stake_delegation_t const * old_delegation = fd_stake_delegation_root_query( stake_delegations, &stake_delegation->stake_account );
     472           3 :       if( FD_LIKELY( old_delegation ) ) {
     473           3 :         fd_stake_history_entry_t old_entry = fd_stakes_activating_and_deactivating( old_delegation, epoch, stake_history, warmup_cooldown_rate_epoch );
     474           3 :         stake_delegations->effective_stake    -= old_entry.effective;
     475           3 :         stake_delegations->activating_stake   -= old_entry.activating;
     476           3 :         stake_delegations->deactivating_stake -= old_entry.deactivating;
     477           3 :       }
     478           3 :       fd_stake_delegations_remove( stake_delegations, &stake_delegation->stake_account );
     479           3 :     }
     480          30 :   }
     481          72 : }
     482             : 
     483             : /* Combined base+delta iterator */
     484             : 
     485             : fd_stake_delegation_t const *
     486         369 : fd_stake_delegations_iter_ele( fd_stake_delegations_iter_t * iter ) {
     487         369 :   ulong idx = root_map_iter_idx( iter->iter, iter->root_map, iter->root_pool );
     488         369 :   fd_stake_delegation_t * stake_delegation = root_pool_ele( iter->root_pool, idx );
     489         369 :   if( FD_UNLIKELY( stake_delegation->delta_idx!=UINT_MAX ) ) {
     490         132 :     return (fd_stake_delegation_t *)delta_pool_ele( iter->delta_pool, stake_delegation->delta_idx );
     491         132 :   }
     492         237 :   return stake_delegation;
     493         369 : }
     494             : 
     495             : ulong
     496          27 : fd_stake_delegations_iter_idx( fd_stake_delegations_iter_t * iter ) {
     497          27 :   return root_map_iter_idx( iter->iter, iter->root_map, iter->root_pool );
     498          27 : }
     499             : 
     500             : static void
     501         450 : skip_tombstones( fd_stake_delegations_iter_t * iter ) {
     502         462 :   while( !root_map_iter_done( iter->iter, iter->root_map, iter->root_pool ) ) {
     503         381 :     fd_stake_delegation_t *       root_delegation = root_map_iter_ele( iter->iter, iter->root_map, iter->root_pool );
     504         381 :     fd_stake_delegation_t const * ele             = (root_delegation->delta_idx != UINT_MAX)
     505         381 :       ? (fd_stake_delegation_t const *)delta_pool_ele( iter->delta_pool, root_delegation->delta_idx )
     506         381 :       : (fd_stake_delegation_t const *)root_delegation;
     507         381 :     if( FD_LIKELY( !ele->is_tombstone ) ) return;
     508          12 :     iter->iter = root_map_iter_next( iter->iter, iter->root_map, iter->root_pool );
     509          12 :   }
     510         450 : }
     511             : 
     512             : fd_stake_delegations_iter_t *
     513             : fd_stake_delegations_iter_init( fd_stake_delegations_iter_t *  iter,
     514         222 :                                 fd_stake_delegations_t const * stake_delegations ) {
     515         222 :   if( FD_UNLIKELY( !stake_delegations ) ) {
     516           0 :     FD_LOG_CRIT(( "NULL stake_delegations" ));
     517           0 :   }
     518             : 
     519         222 :   iter->root_map   = get_root_map( stake_delegations );
     520         222 :   iter->root_pool  = get_root_pool( stake_delegations );
     521         222 :   iter->iter       = root_map_iter_init( iter->root_map, iter->root_pool );
     522         222 :   iter->delta_pool = get_delta_pool( stake_delegations );
     523             : 
     524         222 :   skip_tombstones( iter );
     525             : 
     526         222 :   return iter;
     527         222 : }
     528             : 
     529             : void
     530         228 : fd_stake_delegations_iter_next( fd_stake_delegations_iter_t * iter ) {
     531         228 :   iter->iter = root_map_iter_next( iter->iter, iter->root_map, iter->root_pool );
     532         228 :   skip_tombstones( iter );
     533         228 : }
     534             : 
     535             : int
     536         450 : fd_stake_delegations_iter_done( fd_stake_delegations_iter_t * iter ) {
     537         450 :   return root_map_iter_done( iter->iter, iter->root_map, iter->root_pool );
     538         450 : }
     539             : 
     540             : void
     541             : fd_stake_delegations_mark_delta( fd_stake_delegations_t *   stake_delegations,
     542             :                                  ulong                      epoch,
     543             :                                  fd_stake_history_t const * stake_history,
     544             :                                  ulong *                    warmup_cooldown_rate_epoch,
     545         144 :                                  ushort                     fork_idx ) {
     546             : 
     547         144 :   root_map_t *            root_map   = get_root_map( stake_delegations );
     548         144 :   fd_stake_delegation_t * root_pool  = get_root_pool( stake_delegations );
     549         144 :   fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
     550         144 :   fork_dlist_t *          fork_dlist = get_fork_dlist( stake_delegations, fork_idx );
     551             : 
     552         144 :   for( fork_dlist_iter_t iter = fork_dlist_iter_fwd_init( fork_dlist, delta_pool );
     553         309 :        !fork_dlist_iter_done( iter, fork_dlist, delta_pool );
     554         165 :        iter = fork_dlist_iter_fwd_next( iter, fork_dlist, delta_pool ) ) {
     555         165 :     fd_stake_delegation_t * delta_delegation = fork_dlist_iter_ele( iter, fork_dlist, delta_pool );
     556             : 
     557         165 :     fd_stake_delegation_t * base_delegation = root_map_ele_query( root_map, &delta_delegation->stake_account, NULL, root_pool);
     558         165 :     if( FD_UNLIKELY( !base_delegation ) ) {
     559          66 :       base_delegation                = root_pool_ele_acquire( root_pool );
     560          66 :       base_delegation->stake_account = delta_delegation->stake_account;
     561          66 :       base_delegation->dne_in_root   = 1;
     562          66 :       base_delegation->delta_idx     = (uint)delta_pool_idx( delta_pool, delta_delegation );
     563          66 :       root_map_ele_insert( root_map, base_delegation, root_pool );
     564          99 :     } else {
     565             :       /* Only subtract the old version's stake if it's not a tombstone.*/
     566          99 :       fd_stake_delegation_t *  old_delegation = base_delegation->delta_idx==UINT_MAX ? base_delegation : delta_pool_ele( delta_pool, base_delegation->delta_idx );
     567          99 :       if( FD_LIKELY( base_delegation->delta_idx==UINT_MAX || !old_delegation->is_tombstone ) ) {
     568          90 :         fd_stake_history_entry_t old_entry      = fd_stakes_activating_and_deactivating( old_delegation, epoch, stake_history, warmup_cooldown_rate_epoch );
     569          90 :         stake_delegations->effective_stake    -= old_entry.effective;
     570          90 :         stake_delegations->activating_stake   -= old_entry.activating;
     571          90 :         stake_delegations->deactivating_stake -= old_entry.deactivating;
     572          90 :       }
     573             :       /* Update the base delegation to point to the new version. */
     574          99 :       base_delegation->delta_idx = (uint)delta_pool_idx( delta_pool, delta_delegation );
     575          99 :     }
     576             : 
     577             :     /* Add the new version's stake to the totals (as long as it's not a
     578             :        tombstone).*/
     579         165 :     if( FD_LIKELY( !delta_delegation->is_tombstone ) ) {
     580         141 :       fd_stake_history_entry_t new_entry = fd_stakes_activating_and_deactivating( delta_delegation, epoch, stake_history, warmup_cooldown_rate_epoch );
     581         141 :       stake_delegations->effective_stake    += new_entry.effective;
     582         141 :       stake_delegations->activating_stake   += new_entry.activating;
     583         141 :       stake_delegations->deactivating_stake += new_entry.deactivating;
     584         141 :     }
     585         165 :   }
     586         144 : }
     587             : 
     588             : void
     589             : fd_stake_delegations_unmark_delta( fd_stake_delegations_t *   stake_delegations,
     590             :                                    ulong                      epoch,
     591             :                                    fd_stake_history_t const * stake_history,
     592             :                                    ulong *                    warmup_cooldown_rate_epoch,
     593         144 :                                    ushort                     fork_idx ) {
     594             : 
     595         144 :   root_map_t *            root_map   = get_root_map( stake_delegations );
     596         144 :   fd_stake_delegation_t * root_pool  = get_root_pool( stake_delegations );
     597         144 :   fork_dlist_t *          fork_dlist = get_fork_dlist( stake_delegations, fork_idx );
     598         144 :   fd_stake_delegation_t * delta_pool = get_delta_pool( stake_delegations );
     599             : 
     600         144 :   for( fork_dlist_iter_t iter = fork_dlist_iter_fwd_init( fork_dlist, delta_pool );
     601         309 :        !fork_dlist_iter_done( iter, fork_dlist, delta_pool );
     602         165 :        iter = fork_dlist_iter_fwd_next( iter, fork_dlist, delta_pool ) ) {
     603         165 :     fd_stake_delegation_t * delta_delegation = fork_dlist_iter_ele( iter, fork_dlist, delta_pool );
     604             : 
     605         165 :     fd_stake_delegation_t * base_delegation = root_map_ele_query( root_map, &delta_delegation->stake_account, NULL, root_pool );
     606         165 :     if( FD_UNLIKELY( !base_delegation ) ) {
     607           0 :       continue;
     608           0 :     }
     609             : 
     610         165 :     uint delta_idx = (uint)delta_pool_idx( delta_pool, delta_delegation );
     611         165 :     if( FD_UNLIKELY( base_delegation->delta_idx!=delta_idx ) ) continue;
     612             : 
     613         117 :     if( FD_UNLIKELY( base_delegation->dne_in_root )) {
     614          66 :       if( FD_LIKELY( !delta_delegation->is_tombstone ) ) {
     615          63 :         fd_stake_history_entry_t entry = fd_stakes_activating_and_deactivating( delta_delegation, epoch, stake_history, warmup_cooldown_rate_epoch );
     616          63 :         stake_delegations->effective_stake    -= entry.effective;
     617          63 :         stake_delegations->activating_stake   -= entry.activating;
     618          63 :         stake_delegations->deactivating_stake -= entry.deactivating;
     619          63 :       }
     620             : 
     621          66 :       base_delegation->dne_in_root = 0;
     622          66 :       base_delegation->delta_idx   = UINT_MAX;
     623          66 :       root_map_ele_remove( root_map, &delta_delegation->stake_account, NULL, root_pool );
     624          66 :       root_pool_ele_release( root_pool, base_delegation );
     625             : 
     626          66 :     } else {
     627          51 :       if( FD_LIKELY( !delta_delegation->is_tombstone ) ) {
     628          39 :         fd_stake_history_entry_t entry = fd_stakes_activating_and_deactivating( delta_delegation, epoch, stake_history, warmup_cooldown_rate_epoch );
     629          39 :         stake_delegations->effective_stake    -= entry.effective;
     630          39 :         stake_delegations->activating_stake   -= entry.activating;
     631          39 :         stake_delegations->deactivating_stake -= entry.deactivating;
     632          39 :       }
     633             : 
     634          51 :       base_delegation->delta_idx = UINT_MAX;
     635             : 
     636          51 :       fd_stake_history_entry_t entry = fd_stakes_activating_and_deactivating( base_delegation, epoch, stake_history, warmup_cooldown_rate_epoch );
     637          51 :       stake_delegations->effective_stake    += entry.effective;
     638          51 :       stake_delegations->activating_stake   += entry.activating;
     639          51 :       stake_delegations->deactivating_stake += entry.deactivating;
     640          51 :     }
     641         117 :   }
     642         144 : }

Generated by: LCOV version 1.14