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

Generated by: LCOV version 1.14