LCOV - code coverage report
Current view: top level - flamenco/stakes - fd_stakes.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 420 0.0 %
Date: 2025-03-20 12:08:36 Functions: 0 12 0.0 %

          Line data    Source code
       1             : #include "fd_stakes.h"
       2             : #include "../runtime/fd_system_ids.h"
       3             : #include "../runtime/context/fd_exec_epoch_ctx.h"
       4             : #include "../runtime/context/fd_exec_slot_ctx.h"
       5             : #include "../runtime/program/fd_stake_program.h"
       6             : #include "../runtime/sysvar/fd_sysvar_stake_history.h"
       7             : 
       8             : /* fd_stakes_accum_by_node converts Stakes (unordered list of (vote acc,
       9             :    active stake) tuples) to StakedNodes (rbtree mapping (node identity)
      10             :    => (active stake) ordered by node identity).  Returns the tree root. */
      11             : 
      12             : static fd_stake_weight_t_mapnode_t *
      13             : fd_stakes_accum_by_node( fd_vote_accounts_t const *    in,
      14             :                          fd_stake_weight_t_mapnode_t * out_pool,
      15           0 :                          fd_spad_t *                   runtime_spad ) {
      16             : 
      17             :   /* Stakes::staked_nodes(&self: Stakes) -> HashMap<Pubkey, u64> */
      18             : 
      19           0 :   fd_vote_accounts_pair_t_mapnode_t * in_pool = in->vote_accounts_pool;
      20           0 :   fd_vote_accounts_pair_t_mapnode_t * in_root = in->vote_accounts_root;
      21             : 
      22             :   /* VoteAccounts::staked_nodes(&self: VoteAccounts) -> HashMap<Pubkey, u64> */
      23             : 
      24             :   /* For each active vote account, accumulate (node_identity, stake) by
      25             :      summing stake. */
      26             : 
      27           0 :   fd_stake_weight_t_mapnode_t * out_root = NULL;
      28             : 
      29           0 :   for( fd_vote_accounts_pair_t_mapnode_t * n = fd_vote_accounts_pair_t_map_minimum( in_pool, in_root );
      30           0 :                                            n;
      31           0 :                                            n = fd_vote_accounts_pair_t_map_successor( in_pool, n ) ) {
      32             : 
      33             :     /* ... filter(|(stake, _)| *stake != 0u64) */
      34           0 :     if( n->elem.stake == 0UL ) continue;
      35             : 
      36           0 :     fd_bincode_decode_ctx_t ctx = {
      37           0 :       .data    = n->elem.value.data,
      38           0 :       .dataend = n->elem.value.data + n->elem.value.data_len,
      39           0 :     };
      40             : 
      41           0 :     ulong total_sz = 0UL;
      42           0 :     int   err      = fd_vote_state_versioned_decode_footprint( &ctx, &total_sz );
      43           0 :     if( FD_UNLIKELY( err ) ) {
      44           0 :       FD_LOG_ERR(( "Failed to decode vote account %s (%d)", FD_BASE58_ENC_32_ALLOCA( n->elem.key.key ), err ));
      45           0 :     }
      46             : 
      47           0 :     uchar * mem = fd_spad_alloc( runtime_spad, fd_vote_state_versioned_align(), total_sz );
      48           0 :     if( FD_UNLIKELY( !mem ) ) {
      49           0 :       FD_LOG_ERR(( "Failed to allocate memory for vote account %s", FD_BASE58_ENC_32_ALLOCA( n->elem.key.key ) ));
      50           0 :     }
      51             : 
      52           0 :     fd_vote_state_versioned_t * vsv = fd_vote_state_versioned_decode( mem, &ctx );
      53             : 
      54           0 :     fd_pubkey_t node_pubkey;
      55           0 :     switch( vsv->discriminant ) {
      56           0 :       case fd_vote_state_versioned_enum_v0_23_5:
      57           0 :         node_pubkey = vsv->inner.v0_23_5.node_pubkey;
      58           0 :         break;
      59           0 :       case fd_vote_state_versioned_enum_v1_14_11:
      60           0 :         node_pubkey = vsv->inner.v1_14_11.node_pubkey;
      61           0 :         break;
      62           0 :       case fd_vote_state_versioned_enum_current:
      63           0 :         node_pubkey = vsv->inner.current.node_pubkey;
      64           0 :         break;
      65           0 :       default:
      66           0 :         __builtin_unreachable();
      67           0 :     }
      68             : 
      69             : 
      70             :     /* Extract node pubkey */
      71             : 
      72           0 :     fd_pubkey_t null_key = {0};
      73           0 :     if( memcmp( &node_pubkey, null_key.uc, sizeof(fd_pubkey_t) ) == 0 ) {
      74           0 :       FD_LOG_WARNING(( "vote account %s skipped", FD_BASE58_ENC_32_ALLOCA( n->elem.key.key ) ));
      75           0 :       continue;
      76           0 :     }
      77             :     /* Check if node identity was previously visited */
      78           0 :     fd_stake_weight_t_mapnode_t * query = fd_stake_weight_t_map_acquire( out_pool );
      79           0 :     if( FD_UNLIKELY( !query ) ) {
      80           0 :       FD_LOG_ERR(( "fd_stakes_accum_by_node() failed" ));
      81           0 :     }
      82             : 
      83           0 :     query->elem.key = node_pubkey;
      84           0 :     fd_stake_weight_t_mapnode_t * node = fd_stake_weight_t_map_find( out_pool, out_root, query );
      85             : 
      86           0 :     if( FD_UNLIKELY( node ) ) {
      87             :       /* Accumulate to previously created entry */
      88           0 :       fd_stake_weight_t_map_release( out_pool, query );
      89           0 :       node->elem.stake += n->elem.stake;
      90           0 :     } else {
      91             :       /* Create new entry */
      92           0 :       node = query;
      93           0 :       node->elem.stake = n->elem.stake;
      94           0 :       fd_stake_weight_t_map_insert( out_pool, &out_root, node );
      95           0 :     }
      96           0 :   }
      97             : 
      98           0 :   return out_root;
      99           0 : }
     100             : 
     101             : /* fd_stake_weight_sort sorts the given array of stake weights with
     102             :    length stakes_cnt by tuple (stake, pubkey) in descending order. */
     103             : 
     104             : FD_FN_CONST static int
     105             : fd_stakes_sort_before( fd_stake_weight_t a,
     106           0 :                        fd_stake_weight_t b ) {
     107             : 
     108           0 :   if( a.stake > b.stake ) return 1;
     109           0 :   if( a.stake < b.stake ) return 0;
     110           0 :   if( memcmp( &a.key, &b.key, 32UL )>0 ) return 1;
     111           0 :   return 0;
     112           0 : }
     113             : 
     114             : #define SORT_NAME        fd_stakes_sort
     115           0 : #define SORT_KEY_T       fd_stake_weight_t
     116           0 : #define SORT_BEFORE(a,b) fd_stakes_sort_before( (a), (b) )
     117             : #include "../../util/tmpl/fd_sort.c"
     118             : 
     119             : void
     120             : fd_stake_weight_sort( fd_stake_weight_t * stakes,
     121           0 :                       ulong               stakes_cnt ) {
     122           0 :   fd_stakes_sort_inplace( stakes, stakes_cnt );
     123           0 : }
     124             : 
     125             : /* fd_stakes_export_sorted converts StakedNodes (rbtree mapping
     126             :    (node identity) => (active stake) from fd_stakes_accum_by_node) to
     127             :    a list of fd_stake_weights_t. */
     128             : 
     129             : static ulong
     130             : fd_stakes_export( fd_stake_weight_t_mapnode_t const * const in_pool,
     131             :                   fd_stake_weight_t_mapnode_t const * const root,
     132           0 :                   fd_stake_weight_t *           const out ) {
     133             : 
     134           0 :   fd_stake_weight_t * out_end = out;
     135             : 
     136           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 ) ) {
     137           0 :     *out_end++ = ele->elem;
     138           0 :   }
     139             : 
     140           0 :   return (ulong)( out_end - out );
     141           0 : }
     142             : 
     143             : ulong
     144             : fd_stake_weights_by_node( fd_vote_accounts_t const * accs,
     145             :                           fd_stake_weight_t *        weights,
     146           0 :                           fd_spad_t *                runtime_spad ) {
     147             : 
     148             :   /* Estimate size required to store temporary data structures */
     149             : 
     150             :   /* TODO size is the wrong method name for this */
     151           0 :   ulong vote_acc_cnt = fd_vote_accounts_pair_t_map_size( accs->vote_accounts_pool, accs->vote_accounts_root );
     152             : 
     153           0 :   ulong rb_align     = fd_stake_weight_t_map_align();
     154           0 :   ulong rb_footprint = fd_stake_weight_t_map_footprint( vote_acc_cnt );
     155             : 
     156             :   /* Create rb tree */
     157             : 
     158           0 :   void * pool_mem = fd_spad_alloc( runtime_spad, rb_align, rb_footprint );
     159           0 :   pool_mem = fd_stake_weight_t_map_new( pool_mem, vote_acc_cnt );
     160           0 :   fd_stake_weight_t_mapnode_t * pool = fd_stake_weight_t_map_join( pool_mem );
     161           0 :   if( FD_UNLIKELY( !pool_mem ) ) FD_LOG_CRIT(( "fd_stake_weights_new() failed" ));
     162             : 
     163             :   /* Accumulate stakes to rb tree */
     164             : 
     165           0 :   fd_stake_weight_t_mapnode_t const * root = fd_stakes_accum_by_node( accs, pool, runtime_spad );
     166             : 
     167             :   /* Export to sorted list */
     168             : 
     169           0 :   ulong weights_cnt = fd_stakes_export( pool, root, weights );
     170           0 :   fd_stake_weight_sort( weights, weights_cnt );
     171             : 
     172           0 :   return weights_cnt;
     173           0 : }
     174             : 
     175             : /* Helper function to deserialize a vote account. If successful, populates vote account info in `elem`
     176             :    and saves the decoded vote state in `vote_state` */
     177             : static fd_vote_state_versioned_t *
     178             : deserialize_and_update_vote_account( fd_exec_slot_ctx_t *                slot_ctx,
     179             :                                      fd_vote_accounts_pair_t_mapnode_t * elem,
     180             :                                      fd_stake_weight_t_mapnode_t *       stake_delegations_root,
     181             :                                      fd_stake_weight_t_mapnode_t *       stake_delegations_pool,
     182             :                                      fd_pubkey_t const *                 vote_account_pubkey,
     183           0 :                                      fd_spad_t *                         runtime_spad ) {
     184             : 
     185           0 :   FD_TXN_ACCOUNT_DECL( vote_account );
     186           0 :   if( FD_UNLIKELY( fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, vote_account_pubkey, vote_account ) ) ) {
     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 :   fd_bincode_decode_ctx_t decode = {
     193           0 :     .data    = vote_account->const_data,
     194           0 :     .dataend = vote_account->const_data + vote_account->const_meta->dlen,
     195           0 :   };
     196             : 
     197           0 :   ulong total_sz = 0UL;
     198           0 :   int   err      = fd_vote_state_versioned_decode_footprint( &decode, &total_sz );
     199           0 :   if( FD_UNLIKELY( err ) ) {
     200           0 :     return NULL;
     201           0 :   }
     202             : 
     203           0 :   uchar * mem = fd_spad_alloc( runtime_spad, fd_vote_state_versioned_align(), total_sz );
     204           0 :   if( FD_UNLIKELY( !mem ) ) {
     205           0 :     FD_LOG_ERR(( "Unable to allocate memory" ));
     206           0 :   }
     207             : 
     208           0 :   fd_vote_state_versioned_decode( mem, &decode );
     209             : 
     210             :   // Get the stake amount from the stake delegations map
     211           0 :   fd_stake_weight_t_mapnode_t temp;
     212           0 :   fd_memcpy( &temp.elem.key, vote_account_pubkey, sizeof(fd_pubkey_t) );
     213           0 :   fd_stake_weight_t_mapnode_t * entry = fd_stake_weight_t_map_find( stake_delegations_pool, stake_delegations_root, &temp );
     214           0 :   elem->elem.stake = ( entry==NULL ) ? 0UL : entry->elem.stake;
     215             : 
     216           0 :   return (fd_vote_state_versioned_t *)mem;
     217           0 : }
     218             : 
     219             : static void
     220             : compute_stake_delegations_tpool( void  *tpool,
     221             :                                  ulong t0 FD_PARAM_UNUSED,      ulong t1 FD_PARAM_UNUSED,
     222             :                                  void  *args,
     223             :                                  void  *reduce FD_PARAM_UNUSED, ulong stride FD_PARAM_UNUSED,
     224             :                                  ulong l0 FD_PARAM_UNUSED,      ulong l1 FD_PARAM_UNUSED,
     225             :                                  ulong m0,                      ulong m1,
     226           0 :                                  ulong n0 FD_PARAM_UNUSED,      ulong n1 FD_PARAM_UNUSED  ) {
     227           0 :   fd_epoch_info_t *                temp_info                 = (fd_epoch_info_t *)tpool;
     228           0 :   fd_compute_stake_delegations_t * task_args                 = (fd_compute_stake_delegations_t *)args;
     229           0 :   ulong                            worker_idx                = fd_tile_idx();
     230             : 
     231           0 :   fd_spad_t *                      spad                      = task_args->spads[worker_idx];
     232           0 :   fd_epoch_info_pair_t const *     stake_infos               = temp_info->stake_infos;
     233           0 :   ulong                            epoch                     = task_args->epoch;
     234           0 :   fd_stake_history_t const *       history                   = task_args->stake_history;
     235           0 :   ulong *                          new_rate_activation_epoch = task_args->new_rate_activation_epoch;
     236           0 :   fd_stake_weight_t_mapnode_t *    delegation_pool           = task_args->delegation_pool;
     237           0 :   fd_stake_weight_t_mapnode_t *    delegation_root           = task_args->delegation_root;
     238           0 :   ulong                            vote_states_pool_sz       = task_args->vote_states_pool_sz;
     239             : 
     240           0 : FD_SPAD_FRAME_BEGIN( spad ) {
     241             : 
     242             :   /* Create a temporary <pubkey, stake> map to hold delegations */
     243           0 :   void * mem = fd_spad_alloc( spad, fd_stake_weight_t_map_align(), fd_stake_weight_t_map_footprint( vote_states_pool_sz ) );
     244           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 ) );
     245           0 :   fd_stake_weight_t_mapnode_t * temp_root = NULL;
     246             : 
     247           0 :   fd_stake_weight_t_mapnode_t temp;
     248           0 :   for( ulong i=m0; i<m1; i++ ) {
     249           0 :     fd_delegation_t const * delegation = &stake_infos[i].stake.delegation;
     250           0 :     fd_memcpy( &temp.elem.key, &delegation->voter_pubkey, sizeof(fd_pubkey_t) );
     251             : 
     252             :     // Skip any delegations that are not in the delegation pool
     253           0 :     fd_stake_weight_t_mapnode_t * delegation_entry = fd_stake_weight_t_map_find( delegation_pool, delegation_root, &temp );
     254           0 :     if( FD_UNLIKELY( delegation_entry==NULL ) ) {
     255           0 :       continue;
     256           0 :     }
     257             : 
     258           0 :     fd_stake_history_entry_t new_entry = fd_stake_activating_and_deactivating( delegation, epoch, history, new_rate_activation_epoch );
     259           0 :     delegation_entry = fd_stake_weight_t_map_find( temp_pool, temp_root, &temp );
     260           0 :     if( FD_UNLIKELY( delegation_entry==NULL ) ) {
     261           0 :       delegation_entry = fd_stake_weight_t_map_acquire( temp_pool );
     262           0 :       fd_memcpy( &delegation_entry->elem.key, &delegation->voter_pubkey, sizeof(fd_pubkey_t) );
     263           0 :       delegation_entry->elem.stake = new_entry.effective;
     264           0 :       fd_stake_weight_t_map_insert( temp_pool, &temp_root, delegation_entry );
     265           0 :     } else {
     266           0 :       delegation_entry->elem.stake += new_entry.effective;
     267           0 :     }
     268           0 :   }
     269             : 
     270             :   // Update the parent delegation pool with the calculated delegation values
     271           0 :   for( fd_stake_weight_t_mapnode_t * elem = fd_stake_weight_t_map_minimum( temp_pool, temp_root );
     272           0 :                                      elem;
     273           0 :                                      elem = fd_stake_weight_t_map_successor( temp_pool, elem ) ) {
     274           0 :     fd_stake_weight_t_mapnode_t * output_delegation_node = fd_stake_weight_t_map_find( delegation_pool, delegation_root, elem );
     275           0 :     FD_ATOMIC_FETCH_AND_ADD( &output_delegation_node->elem.stake, elem->elem.stake );
     276           0 :   }
     277             : 
     278           0 : } FD_SPAD_FRAME_END;
     279           0 : }
     280             : 
     281             : /*
     282             : Refresh vote accounts.
     283             : 
     284             : This updates the epoch bank stakes vote_accounts cache - that is, the total amount
     285             : of delegated stake each vote account has, using the current delegation values from inside each
     286             : stake account. Contrary to the Agave equivalent, it also merges the stakes cache vote accounts with the
     287             : new vote account keys from this epoch.
     288             : 
     289             : https://github.com/solana-labs/solana/blob/c091fd3da8014c0ef83b626318018f238f506435/runtime/src/stakes.rs#L562 */
     290             : void
     291             : fd_refresh_vote_accounts( fd_exec_slot_ctx_t *       slot_ctx,
     292             :                           fd_stake_history_t const * history,
     293             :                           ulong *                    new_rate_activation_epoch,
     294             :                           fd_epoch_info_t *          temp_info,
     295             :                           fd_tpool_t *               tpool,
     296             :                           fd_spad_t * *              exec_spads,
     297             :                           ulong                      exec_spad_cnt,
     298           0 :                           fd_spad_t *                runtime_spad ) {
     299             : 
     300           0 :   fd_slot_bank_t *  slot_bank  = &slot_ctx->slot_bank;
     301           0 :   fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
     302           0 :   fd_stakes_t *     stakes     = &epoch_bank->stakes;
     303             : 
     304             :   // Initialize a temporary vote states cache
     305           0 :   ulong vote_states_pool_sz   = fd_vote_accounts_pair_t_map_size( stakes->vote_accounts.vote_accounts_pool, stakes->vote_accounts.vote_accounts_root )
     306           0 :                               + fd_account_keys_pair_t_map_size( slot_bank->vote_account_keys.account_keys_pool, slot_bank->vote_account_keys.account_keys_root );
     307           0 :   temp_info->vote_states_root = NULL;
     308           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 ) );
     309           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 ) );
     310             : 
     311             :   /* Create a map of <pubkey, stake> to store the total stake of each vote account. */
     312           0 :   void * mem = fd_spad_alloc( runtime_spad, fd_stake_weight_t_map_align(), fd_stake_weight_t_map_footprint( vote_states_pool_sz ) );
     313           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 ) );
     314           0 :   fd_stake_weight_t_mapnode_t * root = NULL;
     315             : 
     316             :   /* We can optimize this function by only iterating over the vote accounts (since there's much fewer of them) instead of all
     317             :      of the stake accounts, and pre-inserting them into the delegations pool. This way, the delegation calculations can be tpooled. */
     318           0 :   for( fd_vote_accounts_pair_t_mapnode_t * elem = fd_vote_accounts_pair_t_map_minimum( stakes->vote_accounts.vote_accounts_pool, stakes->vote_accounts.vote_accounts_root );
     319           0 :         elem;
     320           0 :         elem = fd_vote_accounts_pair_t_map_successor( stakes->vote_accounts.vote_accounts_pool, elem ) ) {
     321           0 :     fd_stake_weight_t_mapnode_t * entry = fd_stake_weight_t_map_acquire( pool );
     322           0 :     fd_memcpy( &entry->elem.key, &elem->elem.key, sizeof(fd_pubkey_t) );
     323           0 :     entry->elem.stake = 0UL;
     324           0 :     fd_stake_weight_t_map_insert( pool, &root, entry );
     325           0 :   }
     326             : 
     327           0 :   for( fd_account_keys_pair_t_mapnode_t * n = fd_account_keys_pair_t_map_minimum( slot_bank->vote_account_keys.account_keys_pool, slot_bank->vote_account_keys.account_keys_root );
     328           0 :         n;
     329           0 :         n = fd_account_keys_pair_t_map_successor( slot_bank->vote_account_keys.account_keys_pool, n ) ) {
     330           0 :     fd_stake_weight_t_mapnode_t temp;
     331           0 :     fd_memcpy( &temp.elem.key, &n->elem.key, sizeof(fd_pubkey_t) );
     332           0 :     fd_stake_weight_t_mapnode_t * entry = fd_stake_weight_t_map_find( pool, root, &temp );
     333           0 :     if( FD_LIKELY( entry==NULL ) ) {
     334           0 :       entry = fd_stake_weight_t_map_acquire( pool );
     335           0 :       fd_memcpy( &entry->elem.key, &n->elem.key, sizeof(fd_pubkey_t) );
     336           0 :       entry->elem.stake = 0UL;
     337           0 :       fd_stake_weight_t_map_insert( pool, &root, entry );
     338           0 :     }
     339           0 :   }
     340             : 
     341           0 :   ulong worker_cnt = fd_ulong_min( temp_info->stake_infos_len,
     342           0 :                                    fd_ulong_min( fd_tpool_worker_cnt( tpool ), exec_spad_cnt ) );
     343           0 :   fd_compute_stake_delegations_t task_args  = {
     344           0 :     .epoch                     = stakes->epoch,
     345           0 :     .stake_history             = history,
     346           0 :     .new_rate_activation_epoch = new_rate_activation_epoch,
     347           0 :     .delegation_pool           = pool,
     348           0 :     .delegation_root           = root,
     349           0 :     .vote_states_pool_sz       = vote_states_pool_sz,
     350           0 :     .spads                     = exec_spads,
     351           0 :   };
     352             : 
     353             :   // Now we can iterate over each stake delegation in parallel and fill the delegations map
     354           0 :   fd_tpool_exec_all_batch( tpool, 0UL, worker_cnt, compute_stake_delegations_tpool, temp_info, &task_args, NULL, 1UL, 0UL, temp_info->stake_infos_len );
     355             : 
     356             :   // Iterate over each vote account in the epoch stakes cache and populate the new vote accounts pool
     357           0 :   ulong total_epoch_stake = 0UL;
     358           0 :   for( fd_vote_accounts_pair_t_mapnode_t * elem = fd_vote_accounts_pair_t_map_minimum( stakes->vote_accounts.vote_accounts_pool, stakes->vote_accounts.vote_accounts_root );
     359           0 :         elem;
     360           0 :         elem = fd_vote_accounts_pair_t_map_successor( stakes->vote_accounts.vote_accounts_pool, elem ) ) {
     361           0 :     fd_pubkey_t const *         vote_account_pubkey = &elem->elem.key;
     362           0 :     fd_vote_state_versioned_t * vote_state          = deserialize_and_update_vote_account( slot_ctx,
     363           0 :                                                                                            elem,
     364           0 :                                                                                            root,
     365           0 :                                                                                            pool,
     366           0 :                                                                                            vote_account_pubkey,
     367           0 :                                                                                            runtime_spad );
     368           0 :     if( FD_LIKELY( vote_state ) ) {
     369           0 :       total_epoch_stake += elem->elem.stake;
     370             :       // Insert into the temporary vote states cache
     371           0 :       fd_vote_info_pair_t_mapnode_t * new_vote_state_node = fd_vote_info_pair_t_map_acquire( temp_info->vote_states_pool );
     372           0 :       fd_memcpy( &new_vote_state_node->elem.account, vote_account_pubkey, sizeof(fd_pubkey_t) );
     373           0 :       fd_memcpy( &new_vote_state_node->elem.state, vote_state, sizeof(fd_vote_state_versioned_t) );
     374           0 :       fd_vote_info_pair_t_map_insert( temp_info->vote_states_pool, &temp_info->vote_states_root, new_vote_state_node );
     375           0 :     } else {
     376           0 :       FD_LOG_WARNING(( "Failed to deserialize vote account" ));
     377           0 :     }
     378           0 :   }
     379             : 
     380             :   // Update the epoch stakes cache with new vote accounts from the epoch
     381           0 :   for( fd_account_keys_pair_t_mapnode_t * n = fd_account_keys_pair_t_map_minimum( slot_bank->vote_account_keys.account_keys_pool, slot_bank->vote_account_keys.account_keys_root );
     382           0 :         n;
     383           0 :         n = fd_account_keys_pair_t_map_successor( slot_bank->vote_account_keys.account_keys_pool, n ) ) {
     384             : 
     385           0 :     fd_pubkey_t const * vote_account_pubkey = &n->elem.key;
     386           0 :     fd_vote_accounts_pair_t_mapnode_t key;
     387           0 :     fd_memcpy( &key.elem.key, vote_account_pubkey, sizeof(fd_pubkey_t) );
     388             : 
     389             :     /* No need to process duplicate vote account keys. This is a mostly redundant check
     390             :        since upserting vote accounts also checks against the vote stakes, but this is
     391             :        there anyways in case that ever changes */
     392           0 :     if( FD_UNLIKELY( fd_vote_accounts_pair_t_map_find( stakes->vote_accounts.vote_accounts_pool, stakes->vote_accounts.vote_accounts_root, &key ) ) ) {
     393           0 :       continue;
     394           0 :     }
     395             : 
     396           0 :     fd_vote_accounts_pair_t_mapnode_t * new_vote_node = fd_vote_accounts_pair_t_map_acquire( stakes->vote_accounts.vote_accounts_pool );
     397           0 :     fd_vote_state_versioned_t *         vote_state    = deserialize_and_update_vote_account( slot_ctx,
     398           0 :                                                                                              new_vote_node,
     399           0 :                                                                                              root,
     400           0 :                                                                                              pool,
     401           0 :                                                                                              vote_account_pubkey,
     402           0 :                                                                                              runtime_spad );
     403             : 
     404           0 :     if( FD_UNLIKELY( !vote_state ) ) {
     405           0 :       fd_vote_accounts_pair_t_map_release( stakes->vote_accounts.vote_accounts_pool, new_vote_node );
     406           0 :       continue;
     407           0 :     }
     408             : 
     409             :     // Insert into the epoch stakes cache and temporary vote states cache
     410           0 :     fd_vote_accounts_pair_t_map_insert( stakes->vote_accounts.vote_accounts_pool, &stakes->vote_accounts.vote_accounts_root, new_vote_node );
     411           0 :     total_epoch_stake += new_vote_node->elem.stake;
     412             : 
     413           0 :     fd_vote_info_pair_t_mapnode_t * new_vote_state_node = fd_vote_info_pair_t_map_acquire( temp_info->vote_states_pool );
     414           0 :     fd_memcpy( &new_vote_state_node->elem.account, vote_account_pubkey, sizeof(fd_pubkey_t) );
     415           0 :     fd_memcpy( &new_vote_state_node->elem.state, vote_state, sizeof(fd_vote_state_versioned_t) );
     416           0 :     fd_vote_info_pair_t_map_insert( temp_info->vote_states_pool, &temp_info->vote_states_root, new_vote_state_node );
     417           0 :   }
     418             : 
     419           0 :   slot_ctx->epoch_ctx->total_epoch_stake = total_epoch_stake;
     420             : 
     421           0 :   fd_account_keys_pair_t_map_release_tree( slot_bank->vote_account_keys.account_keys_pool, slot_bank->vote_account_keys.account_keys_root );
     422           0 :   slot_bank->vote_account_keys.account_keys_root = NULL;
     423           0 : }
     424             : 
     425             : static void
     426             : accumulate_stake_cache_delegations_tpool( void  *tpool,
     427             :                                           ulong t0 FD_PARAM_UNUSED,      ulong t1 FD_PARAM_UNUSED,
     428             :                                           void  *args,
     429             :                                           void  *reduce FD_PARAM_UNUSED, ulong stride FD_PARAM_UNUSED,
     430             :                                           ulong l0 FD_PARAM_UNUSED,      ulong l1 FD_PARAM_UNUSED,
     431             :                                           ulong m0 FD_PARAM_UNUSED,      ulong m1 FD_PARAM_UNUSED,
     432           0 :                                           ulong n0 FD_PARAM_UNUSED,      ulong n1 FD_PARAM_UNUSED ) {
     433           0 :   ulong                                   worker_idx                = fd_tile_idx();
     434           0 :   fd_delegation_pair_t_mapnode_t **       delegations_roots         = (fd_delegation_pair_t_mapnode_t **)tpool;
     435           0 :   fd_accumulate_delegations_task_args_t * task_args                 = (fd_accumulate_delegations_task_args_t *)args;
     436             : 
     437           0 :   fd_exec_slot_ctx_t const *              slot_ctx                  = task_args->slot_ctx;
     438           0 :   fd_stake_history_t const *              history                   = task_args->stake_history;
     439           0 :   ulong *                                 new_rate_activation_epoch = task_args->new_rate_activation_epoch;
     440           0 :   fd_stake_history_entry_t *              accumulator               = task_args->accumulator;
     441           0 :   fd_spad_t *                             spad                      = task_args->spads[worker_idx];
     442           0 :   fd_delegation_pair_t_mapnode_t *        delegations_pool          = task_args->stake_delegations_pool;
     443           0 :   fd_epoch_info_t *                       temp_info                 = task_args->temp_info;
     444           0 :   ulong                                   epoch                     = task_args->epoch;
     445             : 
     446           0 :   ulong effective    = 0UL;
     447           0 :   ulong activating   = 0UL;
     448           0 :   ulong deactivating = 0UL;
     449             : 
     450           0 :   FD_SPAD_FRAME_BEGIN( spad ) {
     451           0 :     for( fd_delegation_pair_t_mapnode_t * n =  delegations_roots[worker_idx];
     452           0 :                                           n != delegations_roots[worker_idx+1];
     453           0 :                                           n =  fd_delegation_pair_t_map_successor( delegations_pool, n ) ) {
     454             : 
     455           0 :       FD_TXN_ACCOUNT_DECL( acc );
     456           0 :       int rc = fd_acc_mgr_view( slot_ctx->acc_mgr, slot_ctx->funk_txn, &n->elem.account, acc );
     457           0 :       if( FD_UNLIKELY( rc!=FD_ACC_MGR_SUCCESS || acc->const_meta->info.lamports==0UL ) ) {
     458           0 :         continue;
     459           0 :       }
     460             : 
     461           0 :       fd_stake_state_v2_t stake_state;
     462           0 :       rc = fd_stake_get_state( acc, &stake_state );
     463           0 :       if( FD_UNLIKELY( rc != 0 ) ) {
     464           0 :         continue;
     465           0 :       }
     466             : 
     467           0 :       if( FD_UNLIKELY( !fd_stake_state_v2_is_stake( &stake_state ) ) ) {
     468           0 :         continue;
     469           0 :       }
     470             : 
     471           0 :       if( FD_UNLIKELY( stake_state.inner.stake.stake.delegation.stake == 0 ) ) {
     472           0 :         continue;
     473           0 :       }
     474             : 
     475           0 :       fd_delegation_t * delegation = &stake_state.inner.stake.stake.delegation;
     476             : 
     477           0 :       ulong delegation_idx = FD_ATOMIC_FETCH_AND_ADD( &temp_info->stake_infos_len, 1UL );
     478           0 :       fd_memcpy( &temp_info->stake_infos[delegation_idx].stake, &stake_state.inner.stake.stake, sizeof(fd_stake_t) );
     479           0 :       fd_memcpy( &temp_info->stake_infos[delegation_idx].account, &n->elem.account, sizeof(fd_pubkey_t) );
     480             : 
     481           0 :       fd_stake_history_entry_t new_entry = fd_stake_activating_and_deactivating( delegation, epoch, history, new_rate_activation_epoch );
     482           0 :       effective    += new_entry.effective;
     483           0 :       activating   += new_entry.activating;
     484           0 :       deactivating += new_entry.deactivating;
     485           0 :     }
     486             : 
     487           0 :     FD_ATOMIC_FETCH_AND_ADD( &accumulator->effective,    effective );
     488           0 :     FD_ATOMIC_FETCH_AND_ADD( &accumulator->activating,   activating );
     489           0 :     FD_ATOMIC_FETCH_AND_ADD( &accumulator->deactivating, deactivating );
     490             : 
     491           0 :   } FD_SPAD_FRAME_END;
     492           0 : }
     493             : 
     494             : /* Accumulates information about epoch stakes into `temp_info`, which is a temporary cache
     495             :    used to save intermediate state about stake and vote accounts to avoid them from having to
     496             :    be recomputed on every access, especially at the epoch boundary. Also collects stats in `accumulator` */
     497             : void
     498             : fd_accumulate_stake_infos( fd_exec_slot_ctx_t const * slot_ctx,
     499             :                            fd_stakes_t const *        stakes,
     500             :                            fd_stake_history_t const * history,
     501             :                            ulong *                    new_rate_activation_epoch,
     502             :                            fd_stake_history_entry_t * accumulator,
     503             :                            fd_epoch_info_t *          temp_info,
     504             :                            fd_tpool_t *               tpool,
     505             :                            fd_spad_t * *              exec_spads,
     506             :                            ulong                      exec_spads_cnt,
     507           0 :                            fd_spad_t *                runtime_spad ) {
     508             : 
     509           0 :   ulong stake_delegations_pool_sz = fd_delegation_pair_t_map_size( stakes->stake_delegations_pool, stakes->stake_delegations_root );
     510           0 :   if( FD_UNLIKELY( stake_delegations_pool_sz==0UL ) ) {
     511           0 :     return;
     512           0 :   }
     513             : 
     514             :   /* Batch up the stake info accumulations via tpool. Currently this is only marginally more efficient because we
     515             :      do not have access to iterators at a specific index in constant or logarithmic time. */
     516           0 :   ulong worker_cnt                                         = fd_ulong_min( stake_delegations_pool_sz,
     517           0 :                                                                            fd_ulong_min( fd_tpool_worker_cnt( tpool ), exec_spads_cnt ) );
     518           0 :   fd_delegation_pair_t_mapnode_t ** batch_delegation_roots = fd_spad_alloc( runtime_spad, alignof(fd_delegation_pair_t_mapnode_t *),
     519           0 :                                                                                       ( worker_cnt + 1 )*sizeof(fd_delegation_pair_t_mapnode_t *) );
     520             : 
     521           0 :   ulong * idx_starts = fd_spad_alloc( runtime_spad, alignof(ulong), worker_cnt * sizeof(ulong) );
     522             : 
     523             :   // Determine the logical index partitioning of the delegations pool so we know where to start iterating from
     524           0 :   for( ulong i=0UL; i<worker_cnt; i++ ) {
     525           0 :     ulong _idx_end;
     526           0 :     FD_TPOOL_PARTITION( 0UL, stake_delegations_pool_sz, 1UL, i, worker_cnt, idx_starts[i], _idx_end );
     527           0 :     (void)_idx_end;
     528           0 :   }
     529             : 
     530           0 :   ulong batch_idx = 0UL;
     531           0 :   ulong iter_idx  = 0UL;
     532           0 :   for( fd_delegation_pair_t_mapnode_t * n = fd_delegation_pair_t_map_minimum( stakes->stake_delegations_pool, stakes->stake_delegations_root );
     533           0 :       n;
     534           0 :       n = fd_delegation_pair_t_map_successor( stakes->stake_delegations_pool, n ) ) {
     535           0 :     if( iter_idx++==idx_starts[batch_idx] ) {
     536           0 :       batch_delegation_roots[batch_idx++] = n;
     537           0 :     }
     538           0 :   }
     539           0 :   batch_delegation_roots[worker_cnt] = NULL;
     540             : 
     541           0 :   fd_accumulate_delegations_task_args_t task_args = {
     542           0 :     .slot_ctx                  = slot_ctx,
     543           0 :     .stake_history             = history,
     544           0 :     .new_rate_activation_epoch = new_rate_activation_epoch,
     545           0 :     .accumulator               = accumulator,
     546           0 :     .temp_info                 = temp_info,
     547           0 :     .spads                     = exec_spads,
     548           0 :     .stake_delegations_pool    = stakes->stake_delegations_pool,
     549           0 :     .epoch                     = stakes->epoch,
     550           0 :   };
     551             : 
     552           0 :   fd_tpool_exec_all_batch( tpool, 0UL, worker_cnt, accumulate_stake_cache_delegations_tpool, batch_delegation_roots, &task_args, NULL, 1UL, 0UL, stake_delegations_pool_sz );
     553           0 :   temp_info->stake_infos_new_keys_start_idx = temp_info->stake_infos_len;
     554             : 
     555             :   /* The number of account keys aggregated across the epoch is usually small, so there aren't much performance gains from tpooling here. */
     556           0 :   for( fd_account_keys_pair_t_mapnode_t * n = fd_account_keys_pair_t_map_minimum( slot_ctx->slot_bank.stake_account_keys.account_keys_pool, slot_ctx->slot_bank.stake_account_keys.account_keys_root );
     557           0 :        n;
     558           0 :        n = fd_account_keys_pair_t_map_successor( slot_ctx->slot_bank.stake_account_keys.account_keys_pool, n ) ) {
     559           0 :     FD_TXN_ACCOUNT_DECL( acc );
     560           0 :     int rc = fd_acc_mgr_view(slot_ctx->acc_mgr, slot_ctx->funk_txn, &n->elem.key, acc);
     561           0 :     if( FD_UNLIKELY( rc!=FD_ACC_MGR_SUCCESS || acc->const_meta->info.lamports==0UL ) ) {
     562           0 :       continue;
     563           0 :     }
     564             : 
     565           0 :     fd_stake_state_v2_t stake_state;
     566           0 :     rc = fd_stake_get_state( acc, &stake_state );
     567           0 :     if( FD_UNLIKELY( rc != 0) ) {
     568           0 :       continue;
     569           0 :     }
     570             : 
     571           0 :     if( FD_UNLIKELY( !fd_stake_state_v2_is_stake( &stake_state ) ) ) {
     572           0 :       continue;
     573           0 :     }
     574             : 
     575           0 :     if( FD_UNLIKELY( stake_state.inner.stake.stake.delegation.stake==0UL ) ) {
     576           0 :       continue;
     577           0 :     }
     578             : 
     579           0 :     fd_delegation_t * delegation = &stake_state.inner.stake.stake.delegation;
     580           0 :     fd_memcpy(&temp_info->stake_infos[temp_info->stake_infos_len  ].stake.delegation, &stake_state.inner.stake.stake, sizeof(fd_stake_t));
     581           0 :     fd_memcpy(&temp_info->stake_infos[temp_info->stake_infos_len++].account, &n->elem.key, sizeof(fd_pubkey_t));
     582           0 :     fd_stake_history_entry_t new_entry = fd_stake_activating_and_deactivating( delegation, stakes->epoch, history, new_rate_activation_epoch );
     583           0 :     accumulator->effective    += new_entry.effective;
     584           0 :     accumulator->activating   += new_entry.activating;
     585           0 :     accumulator->deactivating += new_entry.deactivating;
     586           0 :   }
     587             : 
     588           0 : }
     589             : 
     590             : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/runtime/src/stakes.rs#L169 */
     591             : void
     592             : fd_stakes_activate_epoch( fd_exec_slot_ctx_t *  slot_ctx,
     593             :                           ulong *               new_rate_activation_epoch,
     594             :                           fd_epoch_info_t *     temp_info,
     595             :                           fd_tpool_t *          tpool,
     596             :                           fd_spad_t * *         exec_spads,
     597             :                           ulong                 exec_spad_cnt,
     598           0 :                           fd_spad_t *           runtime_spad ) {
     599             : 
     600           0 :   fd_epoch_bank_t * epoch_bank = fd_exec_epoch_ctx_epoch_bank( slot_ctx->epoch_ctx );
     601           0 :   fd_stakes_t *     stakes     = &epoch_bank->stakes;
     602             : 
     603             :   /* Current stake delegations: list of all current delegations in stake_delegations
     604             :      https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/runtime/src/stakes.rs#L180 */
     605             :   /* Add a new entry to the Stake History sysvar for the previous epoch
     606             :      https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/runtime/src/stakes.rs#L181-L192 */
     607             : 
     608           0 :   fd_stake_history_t const * history = (fd_stake_history_t const *)fd_sysvar_cache_stake_history( slot_ctx->sysvar_cache );
     609           0 :   if( FD_UNLIKELY( !history ) ) FD_LOG_ERR(( "StakeHistory sysvar is missing from sysvar cache" ));
     610             : 
     611           0 :   ulong stake_delegations_size = fd_delegation_pair_t_map_size(
     612           0 :     stakes->stake_delegations_pool, stakes->stake_delegations_root );
     613           0 :   stake_delegations_size += fd_account_keys_pair_t_map_size(
     614           0 :     slot_ctx->slot_bank.stake_account_keys.account_keys_pool, slot_ctx->slot_bank.stake_account_keys.account_keys_root );
     615           0 :   temp_info->stake_infos_len = 0UL;
     616           0 :   temp_info->stake_infos     = (fd_epoch_info_pair_t *)fd_spad_alloc( runtime_spad, FD_EPOCH_INFO_PAIR_ALIGN, FD_EPOCH_INFO_PAIR_FOOTPRINT*stake_delegations_size );
     617           0 :   fd_memset( temp_info->stake_infos, 0, FD_EPOCH_INFO_PAIR_FOOTPRINT*stake_delegations_size );
     618             : 
     619           0 :   fd_stake_history_entry_t accumulator = {
     620           0 :     .effective    = 0UL,
     621           0 :     .activating   = 0UL,
     622           0 :     .deactivating = 0UL
     623           0 :   };
     624             : 
     625             :   /* Accumulate stats for stake accounts */
     626           0 :   fd_accumulate_stake_infos( slot_ctx,
     627           0 :                              stakes,
     628           0 :                              history,
     629           0 :                              new_rate_activation_epoch,
     630           0 :                              &accumulator,
     631           0 :                              temp_info,
     632           0 :                              tpool,
     633           0 :                              exec_spads,
     634           0 :                              exec_spad_cnt,
     635           0 :                              runtime_spad );
     636             : 
     637             :   /* https://github.com/anza-xyz/agave/blob/v2.1.6/runtime/src/stakes.rs#L359 */
     638           0 :   fd_stake_history_entry_t new_elem = {
     639           0 :     .epoch        = stakes->epoch,
     640           0 :     .effective    = accumulator.effective,
     641           0 :     .activating   = accumulator.activating,
     642           0 :     .deactivating = accumulator.deactivating
     643           0 :   };
     644             : 
     645           0 :   fd_sysvar_stake_history_update( slot_ctx, &new_elem, runtime_spad );
     646             : 
     647             :   /* Refresh the sysvar cache stake history entry after updating the sysvar.
     648             :       We need to do this here because it is used in subsequent places in the epoch boundary. */
     649             :   // fd_stake_history_destroy( slot_ctx->sysvar_cache->val_stake_history );
     650             :   /* TODO:FIXME: FIX THIS*/
     651           0 :   fd_sysvar_cache_restore_stake_history( slot_ctx->sysvar_cache,
     652           0 :                                          slot_ctx->acc_mgr,
     653           0 :                                          slot_ctx->funk_txn,
     654           0 :                                          runtime_spad,
     655           0 :                                          slot_ctx->runtime_wksp );
     656             : 
     657           0 : }
     658             : 
     659             : int
     660             : write_stake_state( fd_txn_account_t *    stake_acc_rec,
     661           0 :                    fd_stake_state_v2_t * stake_state ) {
     662             : 
     663           0 :   ulong encoded_stake_state_size = fd_stake_state_v2_size(stake_state);
     664             : 
     665           0 :   fd_bincode_encode_ctx_t ctx = {
     666           0 :     .data = stake_acc_rec->data,
     667           0 :     .dataend = stake_acc_rec->data + encoded_stake_state_size,
     668           0 :   };
     669           0 :   if( FD_UNLIKELY( fd_stake_state_v2_encode( stake_state, &ctx ) != FD_BINCODE_SUCCESS ) ) {
     670           0 :     FD_LOG_ERR(( "fd_stake_state_encode failed" ));
     671           0 :   }
     672             : 
     673           0 :   return 0;
     674           0 : }

Generated by: LCOV version 1.14