LCOV - code coverage report
Current view: top level - flamenco/stakes - fd_stakes.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 565 0.0 %
Date: 2025-07-01 05:00:49 Functions: 0 15 0.0 %

          Line data    Source code
       1             : #include "fd_stakes.h"
       2             : #include "../runtime/fd_system_ids.h"
       3             : #include "../runtime/context/fd_exec_slot_ctx.h"
       4             : #include "../runtime/program/fd_stake_program.h"
       5             : #include "../runtime/sysvar/fd_sysvar_stake_history.h"
       6             : 
       7             : /* fd_stakes_accum_by_node converts Stakes (unordered list of (vote acc,
       8             :    active stake) tuples) to StakedNodes (rbtree mapping (node identity)
       9             :    => (active stake) ordered by node identity).  Returns the tree root. */
      10             : 
      11             : static fd_stake_weight_t_mapnode_t *
      12             : fd_stakes_accum_by_node( fd_vote_accounts_global_t const * in,
      13             :                          fd_stake_weight_t_mapnode_t *     out_pool,
      14           0 :                          fd_spad_t *                       runtime_spad ) {
      15             : 
      16             :   /* Stakes::staked_nodes(&self: Stakes) -> HashMap<Pubkey, u64> */
      17             : 
      18           0 :   fd_vote_accounts_pair_global_t_mapnode_t * in_pool = fd_vote_accounts_vote_accounts_pool_join( in );
      19           0 :   fd_vote_accounts_pair_global_t_mapnode_t * in_root = fd_vote_accounts_vote_accounts_root_join( in );
      20             : 
      21             :   /* VoteAccounts::staked_nodes(&self: VoteAccounts) -> HashMap<Pubkey, u64> */
      22             : 
      23             :   /* For each active vote account, accumulate (node_identity, stake) by
      24             :      summing stake. */
      25             : 
      26           0 :   fd_stake_weight_t_mapnode_t * out_root = NULL;
      27             : 
      28           0 :   for( fd_vote_accounts_pair_global_t_mapnode_t * n = fd_vote_accounts_pair_global_t_map_minimum( in_pool, in_root );
      29           0 :                                            n;
      30           0 :                                            n = fd_vote_accounts_pair_global_t_map_successor( in_pool, n ) ) {
      31             : 
      32             :     /* ... filter(|(stake, _)| *stake != 0u64) */
      33           0 :     if( n->elem.stake == 0UL ) continue;
      34             : 
      35           0 :     int err;
      36           0 :     uchar * data     = fd_solana_account_data_join( &n->elem.value );
      37           0 :     ulong   data_len = n->elem.value.data_len;
      38             : 
      39           0 :     fd_vote_state_versioned_t * vsv = fd_bincode_decode_spad(
      40           0 :         vote_state_versioned, runtime_spad,
      41           0 :         data,
      42           0 :         data_len,
      43           0 :         &err );
      44           0 :     if( FD_UNLIKELY( err ) ) {
      45           0 :       FD_LOG_ERR(( "Failed to decode vote account %s (%d)", FD_BASE58_ENC_32_ALLOCA( n->elem.key.key ), err ));
      46           0 :     }
      47             : 
      48           0 :     fd_pubkey_t node_pubkey;
      49           0 :     switch( vsv->discriminant ) {
      50           0 :       case fd_vote_state_versioned_enum_v0_23_5:
      51           0 :         node_pubkey = vsv->inner.v0_23_5.node_pubkey;
      52           0 :         break;
      53           0 :       case fd_vote_state_versioned_enum_v1_14_11:
      54           0 :         node_pubkey = vsv->inner.v1_14_11.node_pubkey;
      55           0 :         break;
      56           0 :       case fd_vote_state_versioned_enum_current:
      57           0 :         node_pubkey = vsv->inner.current.node_pubkey;
      58           0 :         break;
      59           0 :       default:
      60           0 :         __builtin_unreachable();
      61           0 :     }
      62             : 
      63             : 
      64             :     /* Extract node pubkey */
      65             : 
      66           0 :     fd_pubkey_t null_key = {0};
      67           0 :     if( memcmp( &node_pubkey, null_key.uc, sizeof(fd_pubkey_t) ) == 0 ) {
      68           0 :       FD_LOG_WARNING(( "vote account %s skipped", FD_BASE58_ENC_32_ALLOCA( n->elem.key.key ) ));
      69           0 :       continue;
      70           0 :     }
      71             :     /* Check if node identity was previously visited */
      72           0 :     fd_stake_weight_t_mapnode_t * query = fd_stake_weight_t_map_acquire( out_pool );
      73           0 :     if( FD_UNLIKELY( !query ) ) {
      74           0 :       FD_LOG_ERR(( "fd_stakes_accum_by_node() failed" ));
      75           0 :     }
      76             : 
      77           0 :     query->elem.key = node_pubkey;
      78             : 
      79           0 :     fd_stake_weight_t_mapnode_t * node = fd_stake_weight_t_map_find( out_pool, out_root, query );
      80             : 
      81           0 :     if( FD_UNLIKELY( node ) ) {
      82             :       /* Accumulate to previously created entry */
      83           0 :       fd_stake_weight_t_map_release( out_pool, query );
      84           0 :       node->elem.stake += n->elem.stake;
      85           0 :     } else {
      86             :       /* Create new entry */
      87           0 :       node = query;
      88           0 :       node->elem.stake = n->elem.stake;
      89           0 :       fd_stake_weight_t_map_insert( out_pool, &out_root, node );
      90           0 :     }
      91           0 :   }
      92             : 
      93           0 :   return out_root;
      94           0 : }
      95             : 
      96             : /* fd_stake_weight_sort sorts the given array of stake weights with
      97             :    length stakes_cnt by tuple (stake, pubkey) in descending order. */
      98             : 
      99             : FD_FN_CONST static int
     100             : fd_stakes_sort_before( fd_stake_weight_t a,
     101           0 :                        fd_stake_weight_t b ) {
     102             : 
     103           0 :   if( a.stake > b.stake ) return 1;
     104           0 :   if( a.stake < b.stake ) return 0;
     105           0 :   if( memcmp( &a.key, &b.key, 32UL )>0 ) return 1;
     106           0 :   return 0;
     107           0 : }
     108             : 
     109             : #define SORT_NAME        fd_stakes_sort
     110           0 : #define SORT_KEY_T       fd_stake_weight_t
     111           0 : #define SORT_BEFORE(a,b) fd_stakes_sort_before( (a), (b) )
     112             : #include "../../util/tmpl/fd_sort.c"
     113             : 
     114             : void
     115             : fd_stake_weight_sort( fd_stake_weight_t * stakes,
     116           0 :                       ulong               stakes_cnt ) {
     117           0 :   fd_stakes_sort_inplace( stakes, stakes_cnt );
     118           0 : }
     119             : 
     120             : /* fd_stakes_export_sorted converts StakedNodes (rbtree mapping
     121             :    (node identity) => (active stake) from fd_stakes_accum_by_node) to
     122             :    a list of fd_stake_weights_t. */
     123             : 
     124             : static ulong
     125             : fd_stakes_export( fd_stake_weight_t_mapnode_t const * const in_pool,
     126             :                   fd_stake_weight_t_mapnode_t const * const root,
     127           0 :                   fd_stake_weight_t *           const out ) {
     128             : 
     129           0 :   fd_stake_weight_t * out_end = out;
     130             : 
     131           0 :   for( fd_stake_weight_t_mapnode_t const * ele = fd_stake_weight_t_map_minimum( (fd_stake_weight_t_mapnode_t *)in_pool, (fd_stake_weight_t_mapnode_t *)root ); ele; ele = (fd_stake_weight_t_mapnode_t *)fd_stake_weight_t_map_successor( (fd_stake_weight_t_mapnode_t *)in_pool, (fd_stake_weight_t_mapnode_t *)ele ) ) {
     132           0 :     *out_end++ = ele->elem;
     133           0 :   }
     134             : 
     135           0 :   return (ulong)( out_end - out );
     136           0 : }
     137             : 
     138             : ulong
     139             : fd_stake_weights_by_node( fd_vote_accounts_global_t const * accs,
     140             :                           fd_stake_weight_t *               weights,
     141           0 :                           fd_spad_t *                       runtime_spad ) {
     142             : 
     143             :   /* Estimate size required to store temporary data structures */
     144             : 
     145           0 :   fd_vote_accounts_pair_global_t_mapnode_t * vote_acc_pool = fd_vote_accounts_vote_accounts_pool_join( accs );
     146           0 :   fd_vote_accounts_pair_global_t_mapnode_t * vote_acc_root = fd_vote_accounts_vote_accounts_root_join( accs );
     147             : 
     148           0 :   ulong vote_acc_cnt = fd_vote_accounts_pair_global_t_map_size( vote_acc_pool, vote_acc_root );
     149             : 
     150           0 :   ulong rb_align     = fd_stake_weight_t_map_align();
     151           0 :   ulong rb_footprint = fd_stake_weight_t_map_footprint( vote_acc_cnt );
     152             : 
     153             :   /* Create rb tree */
     154             : 
     155           0 :   void * pool_mem = fd_spad_alloc( runtime_spad, rb_align, rb_footprint );
     156           0 :   pool_mem = fd_stake_weight_t_map_new( pool_mem, vote_acc_cnt );
     157           0 :   fd_stake_weight_t_mapnode_t * pool = fd_stake_weight_t_map_join( pool_mem );
     158           0 :   if( FD_UNLIKELY( !pool_mem ) ) FD_LOG_CRIT(( "fd_stake_weights_new() failed" ));
     159             : 
     160             :   /* Accumulate stakes to rb tree */
     161             : 
     162           0 :   fd_stake_weight_t_mapnode_t const * root = fd_stakes_accum_by_node( accs, pool, runtime_spad );
     163             : 
     164             :   /* Export to sorted list */
     165             : 
     166           0 :   ulong weights_cnt = fd_stakes_export( pool, root, weights );
     167           0 :   fd_stake_weight_sort( weights, weights_cnt );
     168             : 
     169           0 :   return weights_cnt;
     170           0 : }
     171             : 
     172             : /* Helper function to deserialize a vote account. If successful, populates vote account info in `elem`
     173             :    and saves the decoded vote state in `vote_state` */
     174             : static fd_vote_state_versioned_t *
     175             : deserialize_and_update_vote_account( fd_exec_slot_ctx_t *                       slot_ctx,
     176             :                                      fd_vote_accounts_pair_global_t_mapnode_t * elem,
     177             :                                      fd_stake_weight_t_mapnode_t *              stake_delegations_root,
     178             :                                      fd_stake_weight_t_mapnode_t *              stake_delegations_pool,
     179             :                                      fd_pubkey_t const *                        vote_account_pubkey,
     180           0 :                                      fd_spad_t *                                runtime_spad ) {
     181             : 
     182           0 :   FD_TXN_ACCOUNT_DECL( vote_account );
     183           0 :   if( FD_UNLIKELY( fd_txn_account_init_from_funk_readonly( vote_account,
     184           0 :                                                            vote_account_pubkey,
     185           0 :                                                            slot_ctx->funk,
     186           0 :                                                            slot_ctx->funk_txn ) ) ) {
     187           0 :     FD_LOG_DEBUG(( "Vote account not found" ));
     188           0 :     return NULL;
     189           0 :   }
     190             : 
     191             :   // Deserialize the vote account and ensure its in the correct state
     192           0 :   int err;
     193           0 :   fd_vote_state_versioned_t * res = fd_bincode_decode_spad(
     194           0 :       vote_state_versioned, runtime_spad,
     195           0 :       vote_account->vt->get_data( vote_account ),
     196           0 :       vote_account->vt->get_data_len( vote_account ),
     197           0 :       &err );
     198           0 :   if( FD_UNLIKELY( err ) ) {
     199           0 :     return NULL;
     200           0 :   }
     201             : 
     202             :   // Get the stake amount from the stake delegations map
     203           0 :   fd_stake_weight_t_mapnode_t temp;
     204           0 :   temp.elem.key = *vote_account_pubkey;
     205           0 :   fd_stake_weight_t_mapnode_t * entry = fd_stake_weight_t_map_find( stake_delegations_pool, stake_delegations_root, &temp );
     206           0 :   elem->elem.stake = ( entry==NULL ) ? 0UL : entry->elem.stake;
     207             : 
     208           0 :   return res;
     209           0 : }
     210             : 
     211             : static void
     212             : compute_stake_delegations( fd_epoch_info_t *                temp_info,
     213             :                            fd_compute_stake_delegations_t * task_args,
     214             :                            ulong                            worker_idx,
     215             :                            ulong                            start_idx,
     216           0 :                            ulong                            end_idx ) {
     217             : 
     218             : 
     219           0 :   fd_spad_t *                      spad                      = task_args->spads[worker_idx];
     220           0 :   fd_epoch_info_pair_t const *     stake_infos               = temp_info->stake_infos;
     221           0 :   ulong                            epoch                     = task_args->epoch;
     222           0 :   fd_stake_history_t const *       history                   = task_args->stake_history;
     223           0 :   ulong *                          new_rate_activation_epoch = task_args->new_rate_activation_epoch;
     224           0 :   fd_stake_weight_t_mapnode_t *    delegation_pool           = task_args->delegation_pool;
     225           0 :   fd_stake_weight_t_mapnode_t *    delegation_root           = task_args->delegation_root;
     226           0 :   ulong                            vote_states_pool_sz       = task_args->vote_states_pool_sz;
     227             : 
     228           0 :   FD_SPAD_FRAME_BEGIN( spad ) {
     229             : 
     230             :   /* Create a temporary <pubkey, stake> map to hold delegations */
     231           0 :   void * mem = fd_spad_alloc( spad, fd_stake_weight_t_map_align(), fd_stake_weight_t_map_footprint( vote_states_pool_sz ) );
     232           0 :   fd_stake_weight_t_mapnode_t * temp_pool = fd_stake_weight_t_map_join( fd_stake_weight_t_map_new( mem, vote_states_pool_sz ) );
     233           0 :   fd_stake_weight_t_mapnode_t * temp_root = NULL;
     234             : 
     235           0 :   fd_stake_weight_t_mapnode_t temp;
     236           0 :   for( ulong i=start_idx; i<end_idx; i++ ) {
     237           0 :     fd_delegation_t const * delegation = &stake_infos[i].stake.delegation;
     238           0 :     temp.elem.key = delegation->voter_pubkey;
     239             : 
     240             :     // Skip any delegations that are not in the delegation pool
     241           0 :     fd_stake_weight_t_mapnode_t * delegation_entry = fd_stake_weight_t_map_find( delegation_pool, delegation_root, &temp );
     242           0 :     if( FD_UNLIKELY( delegation_entry==NULL ) ) {
     243           0 :       continue;
     244           0 :     }
     245             : 
     246           0 :     fd_stake_history_entry_t new_entry = fd_stake_activating_and_deactivating( delegation, epoch, history, new_rate_activation_epoch );
     247           0 :     delegation_entry = fd_stake_weight_t_map_find( temp_pool, temp_root, &temp );
     248           0 :     if( FD_UNLIKELY( delegation_entry==NULL ) ) {
     249           0 :       delegation_entry = fd_stake_weight_t_map_acquire( temp_pool );
     250           0 :       delegation_entry->elem.key   = delegation->voter_pubkey;
     251           0 :       delegation_entry->elem.stake = new_entry.effective;
     252           0 :       fd_stake_weight_t_map_insert( temp_pool, &temp_root, delegation_entry );
     253           0 :     } else {
     254           0 :       delegation_entry->elem.stake += new_entry.effective;
     255           0 :     }
     256           0 :   }
     257             : 
     258             :   // Update the parent delegation pool with the calculated delegation values
     259           0 :   for( fd_stake_weight_t_mapnode_t * elem = fd_stake_weight_t_map_minimum( temp_pool, temp_root );
     260           0 :                                       elem;
     261           0 :                                       elem = fd_stake_weight_t_map_successor( temp_pool, elem ) ) {
     262           0 :     fd_stake_weight_t_mapnode_t * output_delegation_node = fd_stake_weight_t_map_find( delegation_pool, delegation_root, elem );
     263           0 :     FD_ATOMIC_FETCH_AND_ADD( &output_delegation_node->elem.stake, elem->elem.stake );
     264           0 :   }
     265             : 
     266           0 :   } FD_SPAD_FRAME_END;
     267             : 
     268           0 : }
     269             : 
     270             : static void
     271             : compute_stake_delegations_tpool_task( void  *tpool,
     272             :                                       ulong t0 FD_PARAM_UNUSED,      ulong t1 FD_PARAM_UNUSED,
     273             :                                       void  *args,
     274             :                                       void  *reduce FD_PARAM_UNUSED, ulong stride FD_PARAM_UNUSED,
     275             :                                       ulong l0 FD_PARAM_UNUSED,      ulong l1 FD_PARAM_UNUSED,
     276             :                                       ulong m0,                      ulong m1,
     277           0 :                                       ulong n0 FD_PARAM_UNUSED,      ulong n1 FD_PARAM_UNUSED  ) {
     278           0 :   fd_epoch_info_t *                temp_info  = (fd_epoch_info_t *)tpool;
     279           0 :   fd_compute_stake_delegations_t * task_args  = (fd_compute_stake_delegations_t *)args;
     280           0 :   ulong                            worker_idx = fd_tile_idx();
     281             : 
     282           0 :   compute_stake_delegations( temp_info, task_args, worker_idx, m0, m1 );
     283           0 : }
     284             : 
     285             : /* Populates vote accounts with updated delegated stake from the next cached epoch stakes into temp_info */
     286             : void
     287             : fd_populate_vote_accounts( fd_exec_slot_ctx_t *       slot_ctx,
     288             :                            fd_stake_history_t const * history,
     289             :                            ulong *                    new_rate_activation_epoch,
     290             :                            fd_epoch_info_t *          temp_info,
     291             :                            fd_tpool_t *               tpool,
     292             :                            fd_spad_t * *              exec_spads,
     293             :                            ulong                      exec_spad_cnt,
     294           0 :                            fd_spad_t *                runtime_spad ) {
     295             : 
     296             : 
     297             :   /* Initialize a temporary vote states cache */
     298           0 :   fd_account_keys_global_t *         vote_account_keys        = fd_bank_vote_account_keys_locking_modify( slot_ctx->bank );
     299           0 :   fd_account_keys_pair_t_mapnode_t * vote_account_keys_pool   = fd_account_keys_account_keys_pool_join( vote_account_keys );
     300           0 :   fd_account_keys_pair_t_mapnode_t * vote_account_keys_root   = fd_account_keys_account_keys_root_join( vote_account_keys );
     301           0 :   ulong                              vote_account_keys_map_sz = vote_account_keys_pool ? fd_account_keys_pair_t_map_size( vote_account_keys_pool, vote_account_keys_root ) : 0UL;
     302             : 
     303           0 :   fd_stakes_global_t const *                 stakes                      = fd_bank_stakes_locking_query( slot_ctx->bank );
     304           0 :   fd_vote_accounts_global_t const *          vote_accounts               = &stakes->vote_accounts;
     305           0 :   fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_pool          = fd_vote_accounts_vote_accounts_pool_join( vote_accounts );
     306           0 :   fd_vote_accounts_pair_global_t_mapnode_t * vote_accounts_root          = fd_vote_accounts_vote_accounts_root_join( vote_accounts );
     307           0 :   ulong                                      vote_accounts_stakes_map_sz = vote_accounts_pool ? fd_vote_accounts_pair_global_t_map_size( vote_accounts_pool, vote_accounts_root ) : 0UL;
     308             : 
     309           0 :   ulong vote_states_pool_sz   = vote_accounts_stakes_map_sz + vote_account_keys_map_sz;
     310           0 :   temp_info->vote_states_root = NULL;
     311           0 :   uchar * pool_mem = fd_spad_alloc( runtime_spad, fd_vote_info_pair_t_map_align(), fd_vote_info_pair_t_map_footprint( vote_states_pool_sz ) );
     312           0 :   temp_info->vote_states_pool = fd_vote_info_pair_t_map_join( fd_vote_info_pair_t_map_new( pool_mem, vote_states_pool_sz ) );
     313             : 
     314             :   /* Create a map of <pubkey, stake> to store the total stake of each vote account. */
     315           0 :   void * mem = fd_spad_alloc( runtime_spad, fd_stake_weight_t_map_align(), fd_stake_weight_t_map_footprint( vote_states_pool_sz ) );
     316           0 :   fd_stake_weight_t_mapnode_t * pool = fd_stake_weight_t_map_join( fd_stake_weight_t_map_new( mem, vote_states_pool_sz ) );
     317           0 :   fd_stake_weight_t_mapnode_t * root = NULL;
     318             : 
     319             :   /* We can optimize this function by only iterating over the vote accounts (since there's much fewer of them) instead of all
     320             :      of the stake accounts, and pre-inserting them into the delegations pool. This way, the delegation calculations can be tpooled. */
     321           0 :   for( fd_vote_accounts_pair_global_t_mapnode_t * elem = fd_vote_accounts_pair_global_t_map_minimum( vote_accounts_pool, vote_accounts_root );
     322           0 :         elem;
     323           0 :         elem = fd_vote_accounts_pair_global_t_map_successor( vote_accounts_pool, elem ) ) {
     324           0 :     fd_stake_weight_t_mapnode_t * entry = fd_stake_weight_t_map_acquire( pool );
     325           0 :     entry->elem.key                     = elem->elem.key;
     326           0 :     entry->elem.stake                   = 0UL;
     327           0 :     fd_stake_weight_t_map_insert( pool, &root, entry );
     328           0 :   }
     329             : 
     330           0 :   fd_bank_stakes_end_locking_query( slot_ctx->bank );
     331             : 
     332           0 :   for( fd_account_keys_pair_t_mapnode_t * n = fd_account_keys_pair_t_map_minimum( vote_account_keys_pool, vote_account_keys_root );
     333           0 :         n;
     334           0 :         n = fd_account_keys_pair_t_map_successor( vote_account_keys_pool, n ) ) {
     335           0 :     fd_stake_weight_t_mapnode_t temp;
     336           0 :     temp.elem.key = n->elem.key;
     337           0 :     fd_stake_weight_t_mapnode_t * entry = fd_stake_weight_t_map_find( pool, root, &temp );
     338           0 :     if( FD_LIKELY( entry==NULL ) ) {
     339           0 :       entry             = fd_stake_weight_t_map_acquire( pool );
     340           0 :       entry->elem.key   = n->elem.key;
     341           0 :       entry->elem.stake = 0UL;
     342           0 :       fd_stake_weight_t_map_insert( pool, &root, entry );
     343           0 :     }
     344           0 :   }
     345             : 
     346           0 :   fd_bank_vote_account_keys_end_locking_modify( slot_ctx->bank );
     347             : 
     348           0 :   fd_compute_stake_delegations_t task_args  = {
     349           0 :     .epoch                     = stakes->epoch,
     350           0 :     .stake_history             = history,
     351           0 :     .new_rate_activation_epoch = new_rate_activation_epoch,
     352           0 :     .delegation_pool           = pool,
     353           0 :     .delegation_root           = root,
     354           0 :     .vote_states_pool_sz       = vote_states_pool_sz,
     355           0 :     .spads                     = exec_spads,
     356           0 :   };
     357             : 
     358           0 :   if( !!tpool ) {
     359           0 :     ulong tpool_worker_cnt = fd_tpool_worker_cnt( tpool );
     360           0 :     ulong worker_cnt       = fd_ulong_min( temp_info->stake_infos_len,
     361           0 :                                            fd_ulong_min( tpool_worker_cnt, exec_spad_cnt ) );
     362             :     // Now we can iterate over each stake delegation in parallel and fill the delegations map
     363           0 :     fd_tpool_exec_all_batch( tpool, 0UL, worker_cnt, compute_stake_delegations_tpool_task, temp_info, &task_args, NULL, 1UL, 0UL, temp_info->stake_infos_len );
     364             : 
     365           0 :   } else {
     366           0 :     compute_stake_delegations( temp_info, &task_args, 0UL, 0UL, temp_info->stake_infos_len );
     367           0 :   }
     368             : 
     369             :   // Iterate over each vote account in the epoch stakes cache and populate the new vote accounts pool
     370             :   /* NOTE: we use epoch_bank->next_epoch_stakes because Agave indexes their epoch stakes cache by leader schedule epoch.
     371             :      This means that the epoch stakes for epoch E are indexed by epoch E+1.
     372             :      This is just a workaround for now.
     373             :      https://github.com/anza-xyz/agave/blob/v2.2.14/runtime/src/bank/partitioned_epoch_rewards/calculation.rs#L309 */
     374           0 :   ulong total_epoch_stake = 0UL;
     375             : 
     376           0 :   fd_vote_accounts_global_t const *          next_epoch_stakes      = fd_bank_next_epoch_stakes_locking_query( slot_ctx->bank );
     377           0 :   fd_vote_accounts_pair_global_t_mapnode_t * next_epoch_stakes_pool = fd_vote_accounts_vote_accounts_pool_join( next_epoch_stakes );
     378           0 :   fd_vote_accounts_pair_global_t_mapnode_t * next_epoch_stakes_root = fd_vote_accounts_vote_accounts_root_join( next_epoch_stakes );
     379             : 
     380           0 :   for( fd_vote_accounts_pair_global_t_mapnode_t * elem = fd_vote_accounts_pair_global_t_map_minimum( next_epoch_stakes_pool, next_epoch_stakes_root );
     381           0 :        elem;
     382           0 :        elem = fd_vote_accounts_pair_global_t_map_successor( next_epoch_stakes_pool, elem ) ) {
     383           0 :     fd_pubkey_t const * vote_account_pubkey = &elem->elem.key;
     384           0 :     FD_TXN_ACCOUNT_DECL( acc );
     385           0 :     int rc = fd_txn_account_init_from_funk_readonly( acc, vote_account_pubkey, slot_ctx->funk, slot_ctx->funk_txn );
     386           0 :     FD_TEST( rc == 0 );
     387           0 :     uchar * data     = fd_solana_account_data_join( &elem->elem.value );
     388           0 :     ulong   data_len = elem->elem.value.data_len;
     389             : 
     390           0 :     int err;
     391           0 :     fd_vote_state_versioned_t * vote_state = fd_bincode_decode_spad( vote_state_versioned,
     392           0 :                                                                      runtime_spad,
     393           0 :                                                                      data,
     394           0 :                                                                      data_len,
     395           0 :                                                                      &err );
     396             : 
     397           0 :     if( FD_LIKELY( vote_state ) ) {
     398           0 :       total_epoch_stake += elem->elem.stake;
     399             :       // Insert into the temporary vote states cache
     400           0 :       fd_vote_info_pair_t_mapnode_t * new_vote_state_node = fd_vote_info_pair_t_map_acquire( temp_info->vote_states_pool );
     401           0 :       new_vote_state_node->elem.account = *vote_account_pubkey;
     402           0 :       new_vote_state_node->elem.state   = *vote_state;
     403           0 :       fd_vote_info_pair_t_map_insert( temp_info->vote_states_pool, &temp_info->vote_states_root, new_vote_state_node );
     404           0 :     } else {
     405           0 :       FD_LOG_WARNING(( "Failed to deserialize vote account" ));
     406           0 :     }
     407           0 :   }
     408           0 :   fd_bank_next_epoch_stakes_end_locking_query( slot_ctx->bank );
     409             : 
     410           0 :   fd_bank_total_epoch_stake_set( slot_ctx->bank, total_epoch_stake );
     411           0 : }
     412             : 
     413             : /*
     414             : Refresh vote accounts.
     415             : 
     416             : This updates the epoch bank stakes vote_accounts cache - that is, the total amount
     417             : of delegated stake each vote account has, using the current delegation values from inside each
     418             : stake account. Contrary to the Agave equivalent, it also merges the stakes cache vote accounts with the
     419             : new vote account keys from this epoch.
     420             : 
     421             : https://github.com/solana-labs/solana/blob/c091fd3da8014c0ef83b626318018f238f506435/runtime/src/stakes.rs#L562 */
     422             : void
     423             : fd_refresh_vote_accounts( fd_exec_slot_ctx_t *       slot_ctx,
     424             :                           fd_stake_history_t const * history,
     425             :                           ulong *                    new_rate_activation_epoch,
     426             :                           fd_epoch_info_t *          temp_info,
     427             :                           fd_tpool_t *               tpool,
     428             :                           fd_spad_t * *              exec_spads,
     429             :                           ulong                      exec_spad_cnt,
     430           0 :                           fd_spad_t *                runtime_spad ) {
     431             : 
     432           0 :   fd_stakes_global_t *                       stakes                    = fd_bank_stakes_locking_modify( slot_ctx->bank );
     433           0 :   fd_vote_accounts_global_t *                vote_accounts             = &stakes->vote_accounts;
     434           0 :   fd_vote_accounts_pair_global_t_mapnode_t * stakes_vote_accounts_pool = fd_vote_accounts_vote_accounts_pool_join( vote_accounts );
     435           0 :   fd_vote_accounts_pair_global_t_mapnode_t * stakes_vote_accounts_root = fd_vote_accounts_vote_accounts_root_join( vote_accounts );
     436             : 
     437           0 :   fd_account_keys_global_t *         vote_account_keys      = fd_bank_vote_account_keys_locking_modify( slot_ctx->bank );
     438           0 :   fd_account_keys_pair_t_mapnode_t * vote_account_keys_pool = fd_account_keys_account_keys_pool_join( vote_account_keys );
     439           0 :   fd_account_keys_pair_t_mapnode_t * vote_account_keys_root = fd_account_keys_account_keys_root_join( vote_account_keys );
     440             : 
     441           0 :   ulong vote_account_keys_map_sz    = !!vote_account_keys_pool ? fd_account_keys_pair_t_map_size( vote_account_keys_pool, vote_account_keys_root ) : 0UL;
     442           0 :   ulong vote_accounts_stakes_map_sz = !!stakes_vote_accounts_pool ? fd_vote_accounts_pair_global_t_map_size( stakes_vote_accounts_pool, stakes_vote_accounts_root ) : 0UL;
     443           0 :   ulong vote_states_pool_sz         = vote_accounts_stakes_map_sz + vote_account_keys_map_sz;
     444             : 
     445             :   /* Initialize a temporary vote states cache */
     446           0 :   temp_info->vote_states_root = NULL;
     447           0 :   uchar * pool_mem = fd_spad_alloc( runtime_spad, fd_vote_info_pair_t_map_align(), fd_vote_info_pair_t_map_footprint( vote_states_pool_sz ) );
     448           0 :   temp_info->vote_states_pool = fd_vote_info_pair_t_map_join( fd_vote_info_pair_t_map_new( pool_mem, vote_states_pool_sz ) );
     449             : 
     450             :   /* Create a map of <pubkey, stake> to store the total stake of each vote account. */
     451           0 :   void * mem = fd_spad_alloc( runtime_spad, fd_stake_weight_t_map_align(), fd_stake_weight_t_map_footprint( vote_states_pool_sz ) );
     452           0 :   fd_stake_weight_t_mapnode_t * pool = fd_stake_weight_t_map_join( fd_stake_weight_t_map_new( mem, vote_states_pool_sz ) );
     453           0 :   fd_stake_weight_t_mapnode_t * root = NULL;
     454             : 
     455             :   /* We can optimize this function by only iterating over the vote accounts (since there's much fewer of them) instead of all
     456             :      of the stake accounts, and pre-inserting them into the delegations pool. This way, the delegation calculations can be tpooled. */
     457           0 :   for( fd_vote_accounts_pair_global_t_mapnode_t * elem = fd_vote_accounts_pair_global_t_map_minimum( stakes_vote_accounts_pool, stakes_vote_accounts_root );
     458           0 :         elem;
     459           0 :         elem = fd_vote_accounts_pair_global_t_map_successor( stakes_vote_accounts_pool, elem ) ) {
     460           0 :     fd_stake_weight_t_mapnode_t * entry = fd_stake_weight_t_map_acquire( pool );
     461           0 :     entry->elem.key                     = elem->elem.key;
     462           0 :     entry->elem.stake                   = 0UL;
     463           0 :     fd_stake_weight_t_map_insert( pool, &root, entry );
     464           0 :   }
     465             : 
     466           0 :   for( fd_account_keys_pair_t_mapnode_t * n = fd_account_keys_pair_t_map_minimum( vote_account_keys_pool, vote_account_keys_root );
     467           0 :         n;
     468           0 :         n = fd_account_keys_pair_t_map_successor( vote_account_keys_pool, n ) ) {
     469           0 :     fd_stake_weight_t_mapnode_t temp;
     470           0 :     temp.elem.key = n->elem.key;
     471           0 :     fd_stake_weight_t_mapnode_t * entry = fd_stake_weight_t_map_find( pool, root, &temp );
     472           0 :     if( FD_LIKELY( entry==NULL ) ) {
     473           0 :       entry             = fd_stake_weight_t_map_acquire( pool );
     474           0 :       entry->elem.key   = n->elem.key;
     475           0 :       entry->elem.stake = 0UL;
     476           0 :       fd_stake_weight_t_map_insert( pool, &root, entry );
     477           0 :     }
     478           0 :   }
     479             : 
     480           0 :   fd_compute_stake_delegations_t task_args  = {
     481           0 :     .epoch                     = stakes->epoch,
     482           0 :     .stake_history             = history,
     483           0 :     .new_rate_activation_epoch = new_rate_activation_epoch,
     484           0 :     .delegation_pool           = pool,
     485           0 :     .delegation_root           = root,
     486           0 :     .vote_states_pool_sz       = vote_states_pool_sz,
     487           0 :     .spads                     = exec_spads,
     488           0 :   };
     489             : 
     490           0 :   if( !!tpool ) {
     491           0 :     ulong tpool_worker_cnt = fd_tpool_worker_cnt( tpool );
     492           0 :     ulong worker_cnt       = fd_ulong_min( temp_info->stake_infos_len,
     493           0 :                                            fd_ulong_min( tpool_worker_cnt, exec_spad_cnt ) );
     494             :     // Now we can iterate over each stake delegation in parallel and fill the delegations map
     495           0 :     fd_tpool_exec_all_batch( tpool, 0UL, worker_cnt, compute_stake_delegations_tpool_task, temp_info, &task_args, NULL, 1UL, 0UL, temp_info->stake_infos_len );
     496             : 
     497           0 :   } else {
     498           0 :     compute_stake_delegations( temp_info, &task_args, 0UL, 0UL, temp_info->stake_infos_len );
     499           0 :   }
     500             : 
     501             :   // Iterate over each vote account in the epoch stakes cache and populate the new vote accounts pool
     502           0 :   ulong total_epoch_stake = 0UL;
     503           0 :   for( fd_vote_accounts_pair_global_t_mapnode_t * elem = fd_vote_accounts_pair_global_t_map_minimum( stakes_vote_accounts_pool, stakes_vote_accounts_root );
     504           0 :        elem;
     505           0 :        elem = fd_vote_accounts_pair_global_t_map_successor( stakes_vote_accounts_pool, elem ) ) {
     506           0 :     fd_pubkey_t const *         vote_account_pubkey = &elem->elem.key;
     507           0 :     fd_vote_state_versioned_t * vote_state          = deserialize_and_update_vote_account( slot_ctx,
     508           0 :                                                                                            elem,
     509           0 :                                                                                            root,
     510           0 :                                                                                            pool,
     511           0 :                                                                                            vote_account_pubkey,
     512           0 :                                                                                            runtime_spad );
     513           0 :     if( FD_LIKELY( vote_state ) ) {
     514           0 :       total_epoch_stake += elem->elem.stake;
     515             :       // Insert into the temporary vote states cache
     516           0 :       fd_vote_info_pair_t_mapnode_t * new_vote_state_node = fd_vote_info_pair_t_map_acquire( temp_info->vote_states_pool );
     517           0 :       new_vote_state_node->elem.account = *vote_account_pubkey;
     518           0 :       new_vote_state_node->elem.state   = *vote_state;
     519           0 :       fd_vote_info_pair_t_map_insert( temp_info->vote_states_pool, &temp_info->vote_states_root, new_vote_state_node );
     520           0 :     } else {
     521           0 :       FD_LOG_WARNING(( "Failed to deserialize vote account" ));
     522           0 :     }
     523           0 :   }
     524             : 
     525             :   // Update the epoch stakes cache with new vote accounts from the epoch
     526           0 :   for( fd_account_keys_pair_t_mapnode_t * n = fd_account_keys_pair_t_map_minimum( vote_account_keys_pool, vote_account_keys_root );
     527           0 :         n;
     528           0 :         n = fd_account_keys_pair_t_map_successor( vote_account_keys_pool, n ) ) {
     529             : 
     530           0 :     fd_pubkey_t const * vote_account_pubkey = &n->elem.key;
     531           0 :     fd_vote_accounts_pair_global_t_mapnode_t key;
     532           0 :     key.elem.key = *vote_account_pubkey;
     533             : 
     534             :     /* No need to process duplicate vote account keys. This is a mostly redundant check
     535             :        since upserting vote accounts also checks against the vote stakes, but this is
     536             :        there anyways in case that ever changes */
     537           0 :     if( FD_UNLIKELY( fd_vote_accounts_pair_global_t_map_find( stakes_vote_accounts_pool, stakes_vote_accounts_root, &key ) ) ) {
     538           0 :       continue;
     539           0 :     }
     540             : 
     541           0 :     fd_vote_accounts_pair_global_t_mapnode_t * new_vote_node = fd_vote_accounts_pair_global_t_map_acquire( stakes_vote_accounts_pool );
     542           0 :     fd_vote_state_versioned_t *                vote_state    = deserialize_and_update_vote_account( slot_ctx,
     543           0 :                                                                                                     new_vote_node,
     544           0 :                                                                                                     root,
     545           0 :                                                                                                     pool,
     546           0 :                                                                                                     vote_account_pubkey,
     547           0 :                                                                                                     runtime_spad );
     548             : 
     549           0 :     if( FD_UNLIKELY( !vote_state ) ) {
     550           0 :       fd_vote_accounts_pair_global_t_map_release( stakes_vote_accounts_pool, new_vote_node );
     551           0 :       continue;
     552           0 :     }
     553             : 
     554             :     // Insert into the epoch stakes cache and temporary vote states cache
     555           0 :     fd_vote_accounts_pair_global_t_map_insert( stakes_vote_accounts_pool, &stakes_vote_accounts_root, new_vote_node );
     556           0 :     total_epoch_stake += new_vote_node->elem.stake;
     557             : 
     558           0 :     fd_vote_info_pair_t_mapnode_t * new_vote_state_node = fd_vote_info_pair_t_map_acquire( temp_info->vote_states_pool );
     559           0 :     new_vote_state_node->elem.account = *vote_account_pubkey;
     560           0 :     new_vote_state_node->elem.state   = *vote_state;
     561           0 :     fd_vote_info_pair_t_map_insert( temp_info->vote_states_pool, &temp_info->vote_states_root, new_vote_state_node );
     562           0 :   }
     563           0 :   fd_vote_accounts_vote_accounts_pool_update( &stakes->vote_accounts, stakes_vote_accounts_pool );
     564           0 :   fd_vote_accounts_vote_accounts_root_update( &stakes->vote_accounts, stakes_vote_accounts_root );
     565             : 
     566           0 :   fd_bank_stakes_end_locking_modify( slot_ctx->bank );
     567             : 
     568           0 :   fd_bank_total_epoch_stake_set( slot_ctx->bank, total_epoch_stake );
     569             : 
     570             :   /* At this point, we need to flush the vote account keys cache */
     571           0 :   vote_account_keys_pool = fd_account_keys_account_keys_pool_join( vote_account_keys );
     572           0 :   vote_account_keys_root = fd_account_keys_account_keys_root_join( vote_account_keys );
     573           0 :   fd_account_keys_pair_t_map_release_tree( vote_account_keys_pool, vote_account_keys_root );
     574           0 :   vote_account_keys_root = NULL;
     575           0 :   fd_account_keys_account_keys_pool_update( vote_account_keys, vote_account_keys_pool );
     576           0 :   fd_account_keys_account_keys_root_update( vote_account_keys, vote_account_keys_root );
     577           0 :   fd_bank_vote_account_keys_end_locking_modify( slot_ctx->bank );
     578           0 : }
     579             : 
     580             : static void
     581             : accumulate_stake_cache_delegations( fd_delegation_pair_t_mapnode_t * *      delegations_roots,
     582             :                                     fd_accumulate_delegations_task_args_t * task_args,
     583             :                                     ulong                                   worker_idx,
     584           0 :                                     fd_delegation_pair_t_mapnode_t *        end_node ) {
     585             : 
     586           0 :   fd_exec_slot_ctx_t const *              slot_ctx                  = task_args->slot_ctx;
     587           0 :   fd_stake_history_t const *              history                   = task_args->stake_history;
     588           0 :   ulong *                                 new_rate_activation_epoch = task_args->new_rate_activation_epoch;
     589           0 :   fd_stake_history_entry_t *              accumulator               = task_args->accumulator;
     590           0 :   fd_spad_t *                             spad                      = task_args->spads[worker_idx];
     591           0 :   fd_delegation_pair_t_mapnode_t *        delegations_pool          = task_args->stake_delegations_pool;
     592           0 :   fd_epoch_info_t *                       temp_info                 = task_args->temp_info;
     593           0 :   ulong                                   epoch                     = task_args->epoch;
     594             : 
     595           0 :   ulong effective    = 0UL;
     596           0 :   ulong activating   = 0UL;
     597           0 :   ulong deactivating = 0UL;
     598             : 
     599           0 :   FD_SPAD_FRAME_BEGIN( spad ) {
     600           0 :     for( fd_delegation_pair_t_mapnode_t * n =  delegations_roots[worker_idx];
     601           0 :                                           n != end_node;
     602           0 :                                           n =  fd_delegation_pair_t_map_successor( delegations_pool, n ) ) {
     603             : 
     604           0 :       FD_TXN_ACCOUNT_DECL( acc );
     605           0 :       int rc = fd_txn_account_init_from_funk_readonly( acc,
     606           0 :                                                        &n->elem.account,
     607           0 :                                                        slot_ctx->funk,
     608           0 :                                                        slot_ctx->funk_txn );
     609           0 :       if( FD_UNLIKELY( rc!=FD_ACC_MGR_SUCCESS || acc->vt->get_lamports( acc )==0UL ) ) {
     610           0 :         FD_LOG_WARNING(("Failed to init account"));
     611           0 :         continue;
     612           0 :       }
     613             : 
     614           0 :       fd_stake_state_v2_t stake_state;
     615           0 :       rc = fd_stake_get_state( acc, &stake_state );
     616           0 :       if( FD_UNLIKELY( rc != 0 ) ) {
     617           0 :         FD_LOG_WARNING(("Failed to get stake state"));
     618           0 :         continue;
     619           0 :       }
     620             : 
     621           0 :       if( FD_UNLIKELY( !fd_stake_state_v2_is_stake( &stake_state ) ) ) {
     622           0 :         FD_LOG_WARNING(("Not a stake"));
     623           0 :         continue;
     624           0 :       }
     625             : 
     626           0 :       if( FD_UNLIKELY( stake_state.inner.stake.stake.delegation.stake == 0 ) ) {
     627           0 :         continue;
     628           0 :       }
     629             : 
     630           0 :       fd_delegation_t * delegation = &stake_state.inner.stake.stake.delegation;
     631             : 
     632           0 :       ulong delegation_idx = FD_ATOMIC_FETCH_AND_ADD( &temp_info->stake_infos_len, 1UL );
     633           0 :       temp_info->stake_infos[delegation_idx].stake   = stake_state.inner.stake.stake;
     634           0 :       temp_info->stake_infos[delegation_idx].account = n->elem.account;
     635             : 
     636           0 :       fd_stake_history_entry_t new_entry = fd_stake_activating_and_deactivating( delegation, epoch, history, new_rate_activation_epoch );
     637           0 :       effective    += new_entry.effective;
     638           0 :       activating   += new_entry.activating;
     639           0 :       deactivating += new_entry.deactivating;
     640           0 :     }
     641             : 
     642           0 :     FD_ATOMIC_FETCH_AND_ADD( &accumulator->effective,    effective );
     643           0 :     FD_ATOMIC_FETCH_AND_ADD( &accumulator->activating,   activating );
     644           0 :     FD_ATOMIC_FETCH_AND_ADD( &accumulator->deactivating, deactivating );
     645             : 
     646           0 :   } FD_SPAD_FRAME_END;
     647             : 
     648           0 : }
     649             : 
     650             : static void FD_FN_UNUSED
     651             : accumulate_stake_cache_delegations_tpool_task( void  *tpool,
     652             :                                                ulong t0 FD_PARAM_UNUSED,      ulong t1 FD_PARAM_UNUSED,
     653             :                                                void  *args,
     654             :                                                void  *reduce FD_PARAM_UNUSED, ulong stride FD_PARAM_UNUSED,
     655             :                                                ulong l0 FD_PARAM_UNUSED,      ulong l1 FD_PARAM_UNUSED,
     656             :                                                ulong m0 FD_PARAM_UNUSED,      ulong m1 FD_PARAM_UNUSED,
     657           0 :                                                ulong n0 FD_PARAM_UNUSED,      ulong n1 FD_PARAM_UNUSED ) {
     658           0 :   fd_delegation_pair_t_mapnode_t * *      delegations_roots = (fd_delegation_pair_t_mapnode_t * *)tpool;
     659           0 :   fd_accumulate_delegations_task_args_t * task_args         = (fd_accumulate_delegations_task_args_t *)args;
     660           0 :   ulong                                   worker_idx        = fd_tile_idx();
     661             : 
     662           0 :   accumulate_stake_cache_delegations( delegations_roots,
     663           0 :                                       task_args,
     664           0 :                                       worker_idx,
     665           0 :                                       delegations_roots[ worker_idx+1UL ] );
     666             : 
     667           0 : }
     668             : 
     669             : /* Accumulates information about epoch stakes into `temp_info`, which is a temporary cache
     670             :    used to save intermediate state about stake and vote accounts to avoid them from having to
     671             :    be recomputed on every access, especially at the epoch boundary. Also collects stats in `accumulator` */
     672             : void
     673             : fd_accumulate_stake_infos( fd_exec_slot_ctx_t const * slot_ctx,
     674             :                            fd_stakes_global_t const * stakes,
     675             :                            fd_stake_history_t const * history,
     676             :                            ulong *                    new_rate_activation_epoch,
     677             :                            fd_stake_history_entry_t * accumulator,
     678             :                            fd_epoch_info_t *          temp_info,
     679             :                            fd_tpool_t *               tpool,
     680             :                            fd_spad_t * *              exec_spads,
     681             :                            ulong                      exec_spads_cnt,
     682           0 :                            fd_spad_t *                runtime_spad ) {
     683             : 
     684           0 :   fd_delegation_pair_t_mapnode_t * stake_delegations_pool = fd_stakes_stake_delegations_pool_join( stakes );
     685           0 :   fd_delegation_pair_t_mapnode_t * stake_delegations_root = fd_stakes_stake_delegations_root_join( stakes );
     686             : 
     687           0 :   ulong stake_delegations_pool_sz = fd_delegation_pair_t_map_size( stake_delegations_pool, stake_delegations_root );
     688           0 :   if( FD_UNLIKELY( stake_delegations_pool_sz==0UL ) ) {
     689           0 :     return;
     690           0 :   }
     691             : 
     692             :   /* Batch up the stake info accumulations via tpool. Currently this is only marginally more efficient because we
     693             :      do not have access to iterators at a specific index in constant or logarithmic time. */
     694           0 :   ulong tpool_worker_cnt                                   = !!tpool ? fd_tpool_worker_cnt( tpool ) : 1UL;
     695           0 :   ulong worker_cnt                                         = fd_ulong_min( stake_delegations_pool_sz,
     696           0 :                                                                            fd_ulong_min( tpool_worker_cnt, exec_spads_cnt ) );
     697           0 :   fd_delegation_pair_t_mapnode_t ** batch_delegation_roots = fd_spad_alloc( runtime_spad, alignof(fd_delegation_pair_t_mapnode_t *),
     698           0 :                                                                                       ( worker_cnt + 1 )*sizeof(fd_delegation_pair_t_mapnode_t *) );
     699             : 
     700           0 :   ulong * idx_starts = fd_spad_alloc( runtime_spad, alignof(ulong), worker_cnt * sizeof(ulong) );
     701             : 
     702             :   // Determine the logical index partitioning of the delegations pool so we know where to start iterating from
     703           0 :   for( ulong i=0UL; i<worker_cnt; i++ ) {
     704           0 :     ulong _idx_end;
     705           0 :     FD_TPOOL_PARTITION( 0UL, stake_delegations_pool_sz, 1UL, i, worker_cnt, idx_starts[i], _idx_end );
     706           0 :     (void)_idx_end;
     707           0 :   }
     708             : 
     709           0 :   ulong batch_idx = 0UL;
     710           0 :   ulong iter_idx  = 0UL;
     711           0 :   for( fd_delegation_pair_t_mapnode_t * n = fd_delegation_pair_t_map_minimum( stake_delegations_pool, stake_delegations_root );
     712           0 :       n;
     713           0 :       n = fd_delegation_pair_t_map_successor( stake_delegations_pool, n ) ) {
     714           0 :     if( iter_idx++==idx_starts[batch_idx] ) {
     715           0 :       batch_delegation_roots[batch_idx++] = n;
     716           0 :     }
     717           0 :   }
     718           0 :   batch_delegation_roots[worker_cnt] = NULL;
     719             : 
     720           0 :   fd_accumulate_delegations_task_args_t task_args = {
     721           0 :     .slot_ctx                  = slot_ctx,
     722           0 :     .stake_history             = history,
     723           0 :     .new_rate_activation_epoch = new_rate_activation_epoch,
     724           0 :     .accumulator               = accumulator,
     725           0 :     .temp_info                 = temp_info,
     726           0 :     .spads                     = exec_spads,
     727           0 :     .stake_delegations_pool    = stake_delegations_pool,
     728           0 :     .epoch                     = stakes->epoch,
     729           0 :   };
     730             : 
     731           0 :   if( !!tpool ) {
     732           0 :     fd_tpool_exec_all_batch( tpool, 0UL, worker_cnt, accumulate_stake_cache_delegations_tpool_task,
     733           0 :                              batch_delegation_roots, &task_args, NULL,
     734           0 :                              1UL, 0UL, stake_delegations_pool_sz );
     735           0 :   } else {
     736           0 :     accumulate_stake_cache_delegations( batch_delegation_roots,
     737           0 :                                         &task_args,
     738           0 :                                         0UL,
     739           0 :                                         NULL );
     740           0 :   }
     741           0 :   temp_info->stake_infos_new_keys_start_idx = temp_info->stake_infos_len;
     742             : 
     743           0 :   fd_account_keys_global_t const *   stake_account_keys = fd_bank_stake_account_keys_locking_query( slot_ctx->bank );
     744           0 :   fd_account_keys_pair_t_mapnode_t * account_keys_pool  = fd_account_keys_account_keys_pool_join( stake_account_keys );
     745           0 :   fd_account_keys_pair_t_mapnode_t * account_keys_root  = fd_account_keys_account_keys_root_join( stake_account_keys );
     746             : 
     747           0 :   if( !account_keys_pool ) {
     748           0 :     fd_bank_stake_account_keys_end_locking_query( slot_ctx->bank );
     749           0 :     return;
     750           0 :   }
     751             : 
     752             :   /* The number of account keys aggregated across the epoch is usually small, so there aren't much performance gains from tpooling here. */
     753           0 :   for( fd_account_keys_pair_t_mapnode_t * n = fd_account_keys_pair_t_map_minimum( account_keys_pool, account_keys_root );
     754           0 :        n;
     755           0 :        n = fd_account_keys_pair_t_map_successor( account_keys_pool, n ) ) {
     756           0 :     FD_TXN_ACCOUNT_DECL( acc );
     757           0 :     int rc = fd_txn_account_init_from_funk_readonly(acc, &n->elem.key, slot_ctx->funk, slot_ctx->funk_txn );
     758           0 :     if( FD_UNLIKELY( rc!=FD_ACC_MGR_SUCCESS || acc->vt->get_lamports( acc )==0UL ) ) {
     759           0 :       continue;
     760           0 :     }
     761             : 
     762           0 :     fd_stake_state_v2_t stake_state;
     763           0 :     rc = fd_stake_get_state( acc, &stake_state );
     764           0 :     if( FD_UNLIKELY( rc != 0 ) ) {
     765           0 :       continue;
     766           0 :     }
     767             : 
     768           0 :     if( FD_UNLIKELY( !fd_stake_state_v2_is_stake( &stake_state ) ) ) {
     769           0 :       continue;
     770           0 :     }
     771             : 
     772           0 :     if( FD_UNLIKELY( stake_state.inner.stake.stake.delegation.stake==0UL ) ) {
     773           0 :       continue;
     774           0 :     }
     775             : 
     776           0 :     fd_delegation_t * delegation = &stake_state.inner.stake.stake.delegation;
     777           0 :     temp_info->stake_infos[temp_info->stake_infos_len  ].stake    = stake_state.inner.stake.stake;
     778           0 :     temp_info->stake_infos[temp_info->stake_infos_len++].account  = n->elem.key;
     779           0 :     fd_stake_history_entry_t new_entry = fd_stake_activating_and_deactivating( delegation, stakes->epoch, history, new_rate_activation_epoch );
     780           0 :     accumulator->effective    += new_entry.effective;
     781           0 :     accumulator->activating   += new_entry.activating;
     782           0 :     accumulator->deactivating += new_entry.deactivating;
     783           0 :   }
     784             : 
     785           0 :   fd_bank_stake_account_keys_end_locking_query( slot_ctx->bank );
     786           0 : }
     787             : 
     788             : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/runtime/src/stakes.rs#L169 */
     789             : void
     790             : fd_stakes_activate_epoch( fd_exec_slot_ctx_t *  slot_ctx,
     791             :                           ulong *               new_rate_activation_epoch,
     792             :                           fd_epoch_info_t *     temp_info,
     793             :                           fd_tpool_t *          tpool,
     794             :                           fd_spad_t * *         exec_spads,
     795             :                           ulong                 exec_spad_cnt,
     796           0 :                           fd_spad_t *           runtime_spad ) {
     797             : 
     798           0 :   fd_stakes_global_t const *       stakes                 = fd_bank_stakes_locking_query( slot_ctx->bank );
     799           0 :   fd_delegation_pair_t_mapnode_t * stake_delegations_pool = fd_stakes_stake_delegations_pool_join( stakes );
     800           0 :   fd_delegation_pair_t_mapnode_t * stake_delegations_root = fd_stakes_stake_delegations_root_join( stakes );
     801             : 
     802           0 :   fd_account_keys_global_t const * stake_account_keys = fd_bank_stake_account_keys_locking_query( slot_ctx->bank );
     803             : 
     804           0 :   fd_account_keys_pair_t_mapnode_t * account_keys_pool = NULL;
     805           0 :   fd_account_keys_pair_t_mapnode_t * account_keys_root = NULL;
     806             : 
     807           0 :   if( stake_account_keys ) {
     808           0 :     account_keys_pool = fd_account_keys_account_keys_pool_join( stake_account_keys );
     809           0 :     account_keys_root = fd_account_keys_account_keys_root_join( stake_account_keys );
     810           0 :   }
     811             : 
     812             :   /* Current stake delegations: list of all current delegations in stake_delegations
     813             :      https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/runtime/src/stakes.rs#L180 */
     814             :   /* Add a new entry to the Stake History sysvar for the previous epoch
     815             :      https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/runtime/src/stakes.rs#L181-L192 */
     816             : 
     817           0 :   fd_stake_history_t const * history = fd_sysvar_stake_history_read( slot_ctx->funk, slot_ctx->funk_txn, runtime_spad );
     818           0 :   if( FD_UNLIKELY( !history ) ) FD_LOG_ERR(( "StakeHistory sysvar is missing from sysvar cache" ));
     819             : 
     820           0 :   ulong stake_delegations_size = fd_delegation_pair_t_map_size(
     821           0 :     stake_delegations_pool, stake_delegations_root );
     822             : 
     823           0 :   stake_delegations_size += !!account_keys_pool ? fd_account_keys_pair_t_map_size( account_keys_pool, account_keys_root ) : 0UL;
     824             : 
     825           0 :   fd_bank_stake_account_keys_end_locking_query( slot_ctx->bank );
     826             : 
     827           0 :   temp_info->stake_infos_len = 0UL;
     828           0 :   temp_info->stake_infos     = (fd_epoch_info_pair_t *)fd_spad_alloc( runtime_spad, FD_EPOCH_INFO_PAIR_ALIGN, sizeof(fd_epoch_info_pair_t)*stake_delegations_size );
     829           0 :   fd_memset( temp_info->stake_infos, 0, sizeof(fd_epoch_info_pair_t)*stake_delegations_size );
     830             : 
     831           0 :   fd_stake_history_entry_t accumulator = {
     832           0 :     .effective    = 0UL,
     833           0 :     .activating   = 0UL,
     834           0 :     .deactivating = 0UL
     835           0 :   };
     836             : 
     837             :   /* Accumulate stats for stake accounts */
     838           0 :   fd_accumulate_stake_infos( slot_ctx,
     839           0 :                              stakes,
     840           0 :                              history,
     841           0 :                              new_rate_activation_epoch,
     842           0 :                              &accumulator,
     843           0 :                              temp_info,
     844           0 :                              tpool,
     845           0 :                              exec_spads,
     846           0 :                              exec_spad_cnt,
     847           0 :                              runtime_spad );
     848             : 
     849             :   /* https://github.com/anza-xyz/agave/blob/v2.1.6/runtime/src/stakes.rs#L359 */
     850           0 :   fd_epoch_stake_history_entry_pair_t new_elem = {
     851           0 :     .epoch        = stakes->epoch,
     852           0 :     .entry        = {
     853           0 :       .effective    = accumulator.effective,
     854           0 :       .activating   = accumulator.activating,
     855           0 :       .deactivating = accumulator.deactivating
     856           0 :     }
     857           0 :   };
     858             : 
     859           0 :   fd_sysvar_stake_history_update( slot_ctx, &new_elem, runtime_spad );
     860             : 
     861           0 :   fd_bank_stakes_end_locking_query( slot_ctx->bank );
     862             : 
     863           0 : }
     864             : 
     865             : int
     866             : write_stake_state( fd_txn_account_t *    stake_acc_rec,
     867           0 :                    fd_stake_state_v2_t * stake_state ) {
     868             : 
     869           0 :   ulong encoded_stake_state_size = fd_stake_state_v2_size(stake_state);
     870             : 
     871           0 :   fd_bincode_encode_ctx_t ctx = {
     872           0 :     .data    = stake_acc_rec->vt->get_data_mut( stake_acc_rec ),
     873           0 :     .dataend = stake_acc_rec->vt->get_data_mut( stake_acc_rec ) + encoded_stake_state_size,
     874           0 :   };
     875           0 :   if( FD_UNLIKELY( fd_stake_state_v2_encode( stake_state, &ctx ) != FD_BINCODE_SUCCESS ) ) {
     876           0 :     FD_LOG_ERR(( "fd_stake_state_encode failed" ));
     877           0 :   }
     878             : 
     879           0 :   return 0;
     880           0 : }

Generated by: LCOV version 1.14