LCOV - code coverage report
Current view: top level - flamenco/stakes - fd_stakes.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 283 590 48.0 %
Date: 2026-05-15 07:18:56 Functions: 11 14 78.6 %

          Line data    Source code
       1             : #include <limits.h>
       2             : 
       3             : #include "fd_stakes.h"
       4             : #include "../runtime/fd_bank.h"
       5             : #include "../runtime/program/vote/fd_vote_state_versioned.h"
       6             : #include "../runtime/program/vote/fd_vote_codec.h"
       7             : #include "../runtime/sysvar/fd_sysvar_stake_history.h"
       8             : #include "../runtime/sysvar/fd_sysvar_epoch_schedule.h"
       9             : #include "../runtime/program/fd_vote_program.h"
      10             : #include "../runtime/fd_runtime_stack.h"
      11             : #include "../runtime/fd_system_ids.h"
      12             : #include "fd_stake_delegations.h"
      13             : #include "../accdb/fd_accdb_sync.h"
      14             : #include "../../util/bits/fd_sat.h"
      15             : #include "fd_stake_types.h"
      16             : #include "fd_top_votes.h"
      17             : 
      18             : /**********************************************************************/
      19             : /* Constants                                                          */
      20             : /**********************************************************************/
      21             : 
      22             : #define DEFAULT_SLASH_PENALTY                      ( 12 )
      23             : 
      24             : /**********************************************************************/
      25             : /* Types                                                              */
      26             : /**********************************************************************/
      27             : 
      28             : struct effective_activating {
      29             :   ulong effective;
      30             :   ulong activating;
      31             : };
      32             : typedef struct effective_activating effective_activating_t;
      33             : 
      34             : /**********************************************************************/
      35             : /* Static helpers                                                     */
      36             : /**********************************************************************/
      37             : 
      38             : static inline double
      39           0 : warmup_cooldown_rate( ulong current_epoch, ulong * new_rate_activation_epoch ) {
      40           0 :   return fd_stake_delegations_warmup_cooldown_rate_to_double(
      41           0 :     fd_stake_warmup_cooldown_rate( current_epoch, new_rate_activation_epoch ) );
      42           0 : }
      43             : 
      44             : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L728
      45             : static effective_activating_t
      46             : stake_and_activating( fd_delegation_t const *    self,
      47             :                       ulong                      target_epoch,
      48             :                       fd_stake_history_t const * history,
      49         597 :                       ulong *                    new_rate_activation_epoch ) {
      50         597 :   ulong delegated_stake = self->stake;
      51             : 
      52         597 :   fd_stake_history_entry_t const * cluster_stake_at_activation_epoch;
      53         597 :   if( self->activation_epoch==ULONG_MAX ) {
      54         234 :     return ( effective_activating_t ){ .effective = delegated_stake, .activating = 0 };
      55         363 :   } else if( self->activation_epoch==self->deactivation_epoch ) {
      56         282 :     return ( effective_activating_t ){ .effective = 0, .activating = 0 };
      57         282 :   } else if( target_epoch==self->activation_epoch ) {
      58           6 :     return ( effective_activating_t ){ .effective = 0, .activating = delegated_stake };
      59          75 :   } else if( target_epoch<self->activation_epoch ) {
      60          18 :     return ( effective_activating_t ){ .effective = 0, .activating = 0 };
      61          57 :   } else if( history &&
      62          57 :               ( cluster_stake_at_activation_epoch = fd_sysvar_stake_history_query(
      63          48 :                     history, self->activation_epoch ) ) ) {
      64           3 :     ulong                            prev_epoch         = self->activation_epoch;
      65           3 :     fd_stake_history_entry_t const * prev_cluster_stake = cluster_stake_at_activation_epoch;
      66             : 
      67           3 :     ulong current_epoch;
      68           3 :     ulong current_effective_stake = 0;
      69           3 :     for( ;; ) {
      70           3 :       current_epoch = prev_epoch + 1;
      71           3 :       if( FD_LIKELY( prev_cluster_stake->activating==0 ) ) {
      72           3 :         break;
      73           3 :       }
      74             : 
      75           0 :       ulong  remaining_activating_stake = delegated_stake - current_effective_stake;
      76           0 :       double weight = (double)remaining_activating_stake / (double)prev_cluster_stake->activating;
      77           0 :       double warmup_cooldown_rate_ =
      78           0 :           warmup_cooldown_rate( current_epoch, new_rate_activation_epoch );
      79             : 
      80           0 :       double newly_effective_cluster_stake =
      81           0 :           (double)prev_cluster_stake->effective * warmup_cooldown_rate_;
      82           0 :       ulong newly_effective_stake =
      83           0 :           fd_ulong_max( fd_rust_cast_double_to_ulong( weight * newly_effective_cluster_stake ), 1 );
      84             : 
      85           0 :       current_effective_stake += newly_effective_stake;
      86           0 :       if( FD_LIKELY( current_effective_stake>=delegated_stake ) ) {
      87           0 :         current_effective_stake = delegated_stake;
      88           0 :         break;
      89           0 :       }
      90             : 
      91           0 :       if( FD_LIKELY( current_epoch>=target_epoch ||
      92           0 :                      current_epoch>=self->deactivation_epoch ) ) {
      93           0 :         break;
      94           0 :       }
      95             : 
      96           0 :       fd_stake_history_entry_t const * current_cluster_stake =
      97           0 :           fd_sysvar_stake_history_query( history, current_epoch );
      98           0 :       if( FD_LIKELY( current_cluster_stake ) ) {
      99           0 :         prev_epoch         = current_epoch;
     100           0 :         prev_cluster_stake = current_cluster_stake;
     101           0 :       } else {
     102           0 :         break;
     103           0 :       }
     104           0 :     }
     105           3 :     return ( effective_activating_t ){ .effective  = current_effective_stake,
     106           3 :                                        .activating = delegated_stake - current_effective_stake };
     107          54 :   } else {
     108          54 :     return ( effective_activating_t ){ .effective = delegated_stake, .activating = 0 };
     109          54 :   }
     110         597 : }
     111             : 
     112             : // https://github.com/anza-xyz/agave/blob/c8685ce0e1bb9b26014f1024de2cd2b8c308cbde/sdk/program/src/stake/state.rs#L641
     113             : fd_stake_history_entry_t
     114             : stake_activating_and_deactivating( fd_delegation_t const *    self,
     115             :                                    ulong                      target_epoch,
     116             :                                    fd_stake_history_t const * stake_history,
     117         597 :                                    ulong *                    new_rate_activation_epoch ) {
     118             : 
     119         597 :   effective_activating_t effective_activating =
     120         597 :       stake_and_activating( self, target_epoch, stake_history, new_rate_activation_epoch );
     121             : 
     122         597 :   ulong effective_stake  = effective_activating.effective;
     123         597 :   ulong activating_stake = effective_activating.activating;
     124             : 
     125         597 :   fd_stake_history_entry_t const * cluster_stake_at_deactivation_epoch = NULL;
     126             : 
     127         597 :   if( target_epoch<self->deactivation_epoch ) {
     128         375 :     if( activating_stake==0 ) {
     129         366 :       return ( fd_stake_history_entry_t ){
     130         366 :           .effective = effective_stake, .deactivating = 0, .activating = 0 };
     131         366 :     } else {
     132           9 :       return ( fd_stake_history_entry_t ){
     133           9 :           .effective = effective_stake, .deactivating = 0, .activating = activating_stake };
     134           9 :     }
     135         375 :   } else if( target_epoch==self->deactivation_epoch ) {
     136           0 :     return ( fd_stake_history_entry_t ){
     137           0 :         .effective = effective_stake, .deactivating = effective_stake, .activating = 0 };
     138         222 :   } else if( stake_history &&
     139         222 :              ( cluster_stake_at_deactivation_epoch = fd_sysvar_stake_history_query( stake_history, self->deactivation_epoch ) ) ) {
     140           0 :     ulong                      prev_epoch         = self->deactivation_epoch;
     141           0 :     fd_stake_history_entry_t const * prev_cluster_stake = cluster_stake_at_deactivation_epoch;
     142             : 
     143           0 :     ulong current_epoch;
     144           0 :     ulong current_effective_stake = effective_stake;
     145           0 :     for( ;; ) {
     146           0 :       current_epoch = prev_epoch + 1;
     147           0 :       if( prev_cluster_stake->deactivating==0 ) break;
     148             : 
     149           0 :       double weight = (double)current_effective_stake / (double)prev_cluster_stake->deactivating;
     150           0 :       double warmup_cooldown_rate_ =
     151           0 :           warmup_cooldown_rate( current_epoch, new_rate_activation_epoch );
     152             : 
     153           0 :       double newly_not_effective_cluster_stake =
     154           0 :           (double)prev_cluster_stake->effective * warmup_cooldown_rate_;
     155           0 :       ulong newly_not_effective_stake =
     156           0 :           fd_ulong_max( fd_rust_cast_double_to_ulong( weight * newly_not_effective_cluster_stake ), 1 );
     157             : 
     158           0 :       current_effective_stake =
     159           0 :           fd_ulong_sat_sub( current_effective_stake, newly_not_effective_stake );
     160           0 :       if( current_effective_stake==0 ) break;
     161             : 
     162           0 :       if( current_epoch>=target_epoch ) break;
     163             : 
     164           0 :       fd_stake_history_entry_t const * current_cluster_stake = NULL;
     165           0 :       if( ( current_cluster_stake = fd_sysvar_stake_history_query(stake_history, current_epoch ) ) ) {
     166           0 :         prev_epoch         = current_epoch;
     167           0 :         prev_cluster_stake = current_cluster_stake;
     168           0 :       } else {
     169           0 :         break;
     170           0 :       }
     171           0 :     }
     172           0 :     return ( fd_stake_history_entry_t ){ .effective    = current_effective_stake,
     173           0 :                                          .deactivating = current_effective_stake,
     174           0 :                                          .activating   = 0 };
     175         222 :   } else {
     176         222 :     return ( fd_stake_history_entry_t ){ .effective = 0, .activating = 0, .deactivating = 0 };
     177         222 :   }
     178         597 : }
     179             : 
     180             : /**********************************************************************/
     181             : /* Public API                                                         */
     182             : /**********************************************************************/
     183             : 
     184             : fd_stake_state_t const *
     185             : fd_stake_state_view( uchar const * data,
     186         138 :                      ulong         data_sz ) {
     187         138 :   if( FD_UNLIKELY( data_sz<4UL ) ) return NULL;
     188         138 :   uint stake_type = FD_LOAD( uint, data );
     189         138 :   switch( stake_type ) {
     190           0 :   case FD_STAKE_STATE_UNINITIALIZED:
     191           0 :     break;
     192           0 :   case FD_STAKE_STATE_INITIALIZED:
     193           0 :     if( FD_UNLIKELY( data_sz<124 ) ) return NULL;
     194           0 :     break;
     195         138 :   case FD_STAKE_STATE_STAKE:
     196         138 :     if( FD_UNLIKELY( data_sz<197 ) ) return NULL;
     197         138 :     break;
     198         138 :   case FD_STAKE_STATE_REWARDS_POOL:
     199           0 :     break;
     200           0 :   default:
     201           0 :     return NULL;
     202         138 :   }
     203         138 :   return fd_type_pun_const( data );
     204         138 : }
     205             : 
     206             : fd_stake_state_t const *
     207          72 : fd_stakes_get_state( fd_account_meta_t const * meta ) {
     208          72 :   if( FD_UNLIKELY( 0!=memcmp( meta->owner, &fd_solana_stake_program_id, sizeof(fd_pubkey_t) ) ) ) {
     209           3 :     return NULL;
     210           3 :   }
     211          69 :   if( FD_UNLIKELY( meta->lamports==0UL ) ) return NULL;
     212          69 :   return fd_stake_state_view( fd_account_data( meta ), meta->dlen );
     213          69 : }
     214             : 
     215             : fd_stake_history_entry_t
     216             : fd_stakes_activating_and_deactivating( fd_stake_delegation_t const * stake_delegation,
     217             :                                        ulong                         target_epoch,
     218             :                                        fd_stake_history_t const *    stake_history,
     219         597 :                                        ulong *                       new_rate_activation_epoch ) {
     220         597 :   fd_delegation_t delegation = {
     221         597 :     .voter_pubkey         = stake_delegation->vote_account,
     222         597 :     .stake                = stake_delegation->stake,
     223         597 :     .deactivation_epoch   = stake_delegation->deactivation_epoch==USHORT_MAX ? ULONG_MAX : stake_delegation->deactivation_epoch,
     224         597 :     .activation_epoch     = stake_delegation->activation_epoch==USHORT_MAX ? ULONG_MAX : stake_delegation->activation_epoch,
     225         597 :     .warmup_cooldown_rate = fd_stake_delegations_warmup_cooldown_rate_to_double( stake_delegation->warmup_cooldown_rate ),
     226         597 :   };
     227             : 
     228         597 :   return stake_activating_and_deactivating(
     229         597 :     &delegation, target_epoch, stake_history, new_rate_activation_epoch );
     230         597 : }
     231             : 
     232             : ulong
     233             : fd_stake_weights_by_node( fd_top_votes_t const *   top_votes_t_2,
     234             :                           fd_vote_stakes_t *       vote_stakes,
     235             :                           ushort                   fork_idx,
     236             :                           fd_vote_stake_weight_t * weights,
     237         129 :                           int                      vat_enabled ) {
     238         129 :   ulong weights_cnt = 0;
     239         129 :   if( vat_enabled ) {
     240           0 :     uchar __attribute__((aligned(FD_TOP_VOTES_ITER_ALIGN))) iter_mem[ FD_TOP_VOTES_ITER_FOOTPRINT ];
     241           0 :     for( fd_top_votes_iter_t * iter = fd_top_votes_iter_init( top_votes_t_2, iter_mem );
     242           0 :          !fd_top_votes_iter_done( top_votes_t_2, iter );
     243           0 :          fd_top_votes_iter_next( top_votes_t_2, iter ) ) {
     244           0 :       fd_pubkey_t pubkey;
     245           0 :       ulong       stake_t_2;
     246           0 :       fd_pubkey_t node_account_t_2;
     247           0 :       fd_top_votes_iter_ele( top_votes_t_2, iter, &pubkey, &node_account_t_2, &stake_t_2, NULL, NULL, NULL );
     248             : 
     249           0 :       fd_memcpy( weights[ weights_cnt ].vote_key.uc, &pubkey, sizeof(fd_pubkey_t) );
     250           0 :       fd_memcpy( weights[ weights_cnt ].id_key.uc, &node_account_t_2, sizeof(fd_pubkey_t) );
     251           0 :       weights[ weights_cnt ].stake = stake_t_2;
     252           0 :       weights_cnt++;
     253           0 :     }
     254         129 :   } else {
     255         129 :     uchar __attribute__((aligned(FD_VOTE_STAKES_ITER_ALIGN))) iter_mem[ FD_VOTE_STAKES_ITER_FOOTPRINT ];
     256         129 :     for( fd_vote_stakes_iter_t * iter = fd_vote_stakes_fork_iter_init( vote_stakes, fork_idx, iter_mem );
     257         264 :          !fd_vote_stakes_fork_iter_done( vote_stakes, fork_idx, iter  );
     258         135 :          fd_vote_stakes_fork_iter_next( vote_stakes, fork_idx, iter ) ) {
     259         135 :       fd_pubkey_t pubkey;
     260         135 :       ulong       stake_t_2;
     261         135 :       fd_pubkey_t node_account_t_2;
     262         135 :       fd_vote_stakes_fork_iter_ele( vote_stakes, fork_idx, iter, &pubkey, NULL, &stake_t_2, NULL, &node_account_t_2, NULL, NULL );
     263         135 :       if( FD_UNLIKELY( !stake_t_2 ) ) continue;
     264             : 
     265         135 :       fd_memcpy( weights[ weights_cnt ].vote_key.uc, &pubkey, sizeof(fd_pubkey_t) );
     266         135 :       fd_memcpy( weights[ weights_cnt ].id_key.uc, &node_account_t_2, sizeof(fd_pubkey_t) );
     267         135 :       weights[ weights_cnt ].stake = stake_t_2;
     268         135 :       weights_cnt++;
     269         135 :     }
     270         129 :     fd_vote_stakes_fork_iter_fini( vote_stakes );
     271         129 :   }
     272             : 
     273         129 :   sort_vote_weights_by_stake_vote_inplace( weights, weights_cnt );
     274             : 
     275             :   /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/leader-schedule/src/lib.rs#L80-L83
     276             :      We do not deduplicate the weights here, unlike Agave, as it is
     277             :      guaranteed there will be no duplicate stake entries for a given fork
     278             :      in the stakes map. */
     279             : 
     280         129 :   return weights_cnt;
     281         129 : }
     282             : 
     283             : ulong
     284             : fd_stake_weights_by_node_next( fd_top_votes_t const *   top_votes_t_1,
     285             :                                fd_vote_stakes_t *       vote_stakes,
     286             :                                ushort                   fork_idx,
     287             :                                fd_vote_stake_weight_t * weights,
     288           0 :                                int                      vat_enabled ) {
     289             : 
     290           0 :   ulong weights_cnt = 0;
     291           0 :   if( vat_enabled ) {
     292           0 :     uchar __attribute__((aligned(FD_TOP_VOTES_ITER_ALIGN))) iter_mem[ FD_TOP_VOTES_ITER_FOOTPRINT ];
     293           0 :     for( fd_top_votes_iter_t * iter = fd_top_votes_iter_init( top_votes_t_1, iter_mem );
     294           0 :          !fd_top_votes_iter_done( top_votes_t_1, iter );
     295           0 :          fd_top_votes_iter_next( top_votes_t_1, iter ) ) {
     296           0 :       fd_pubkey_t pubkey;
     297           0 :       ulong       stake_t_1;
     298           0 :       fd_pubkey_t node_account_t_1;
     299           0 :       fd_top_votes_iter_ele( top_votes_t_1, iter, &pubkey, &node_account_t_1, &stake_t_1, NULL, NULL, NULL );
     300             : 
     301           0 :       fd_memcpy( weights[ weights_cnt ].vote_key.uc, &pubkey, sizeof(fd_pubkey_t) );
     302           0 :       fd_memcpy( weights[ weights_cnt ].id_key.uc, &node_account_t_1, sizeof(fd_pubkey_t) );
     303           0 :       weights[ weights_cnt ].stake = stake_t_1;
     304           0 :       weights_cnt++;
     305           0 :     }
     306           0 :   } else {
     307           0 :     uchar __attribute__((aligned(FD_VOTE_STAKES_ITER_ALIGN))) iter_mem[ FD_VOTE_STAKES_ITER_FOOTPRINT ];
     308           0 :     for( fd_vote_stakes_iter_t * iter = fd_vote_stakes_fork_iter_init( vote_stakes, fork_idx, iter_mem );
     309           0 :          !fd_vote_stakes_fork_iter_done( vote_stakes, fork_idx, iter );
     310           0 :          fd_vote_stakes_fork_iter_next( vote_stakes, fork_idx, iter ) ) {
     311             : 
     312           0 :       fd_pubkey_t pubkey;
     313           0 :       ulong       stake_t_1;
     314           0 :       fd_pubkey_t node_account_t_1;
     315           0 :       fd_vote_stakes_fork_iter_ele( vote_stakes, fork_idx, iter, &pubkey, &stake_t_1, NULL, &node_account_t_1, NULL, NULL, NULL );
     316           0 :       if( FD_UNLIKELY( !stake_t_1 ) ) continue;
     317             : 
     318           0 :       fd_memcpy( weights[ weights_cnt ].vote_key.uc, &pubkey, sizeof(fd_pubkey_t) );
     319           0 :       fd_memcpy( weights[ weights_cnt ].id_key.uc, &node_account_t_1, sizeof(fd_pubkey_t) );
     320           0 :       weights[ weights_cnt ].stake = stake_t_1;
     321           0 :       weights_cnt++;
     322           0 :     }
     323           0 :     fd_vote_stakes_fork_iter_fini( vote_stakes );
     324           0 :   }
     325             : 
     326           0 :   sort_vote_weights_by_stake_vote_inplace( weights, weights_cnt );
     327             : 
     328             :   /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/leader-schedule/src/lib.rs#L80-L83
     329             :      We do not deduplicate the weights here, unlike Agave, as it is
     330             :      guaranteed there will be no duplicate stake entries for a given fork
     331             :      in the stakes map. */
     332             : 
     333           0 :   return weights_cnt;
     334           0 : }
     335             : 
     336             : static void
     337             : get_vote_credits( uchar const *        account_data,
     338             :                   ulong                account_data_len,
     339         135 :                   fd_epoch_credits_t * epoch_credits ) {
     340             : 
     341         135 :   fd_vote_epoch_credits_t const * vote_epoch_credits = fd_vote_account_epoch_credits( account_data, account_data_len, &epoch_credits->cnt );
     342         135 :   FD_TEST( vote_epoch_credits );
     343             : 
     344         135 :   ulong base = epoch_credits->cnt ? vote_epoch_credits[0].prev_credits : 0UL;
     345         168 :   for( ulong i=0UL; i<epoch_credits->cnt; i++ ) {
     346          33 :     fd_vote_epoch_credits_t const * ele        = &vote_epoch_credits[ i ];
     347          33 :     epoch_credits->epoch[ i ]              = (ushort)ele->epoch;
     348          33 :     epoch_credits->credits_delta[ i ]      = (uint)( ele->credits      - base );
     349          33 :     epoch_credits->prev_credits_delta[ i ] = (uint)( ele->prev_credits - base );
     350          33 :   }
     351             : 
     352         135 :   epoch_credits->base_credits = base;
     353         135 : }
     354             : 
     355             : static void
     356             : fd_refresh_vote_accounts_vat( fd_bank_t *                    bank,
     357             :                               fd_accdb_user_t *              accdb,
     358             :                               fd_funk_txn_xid_t const *      xid,
     359             :                               fd_runtime_stack_t *           runtime_stack,
     360             :                               fd_stake_delegations_t const * stake_delegations,
     361             :                               fd_stake_history_t const *     history,
     362           0 :                               ulong *                        new_rate_activation_epoch ) {
     363             : 
     364           0 :   fd_top_votes_t * top_votes_t_1 = fd_bank_top_votes_t_1_modify( bank );
     365           0 :   fd_top_votes_t * top_votes_t_2 = fd_bank_top_votes_t_2_modify( bank );
     366             : 
     367           0 :   uchar __attribute__((aligned(FD_TOP_VOTES_ALIGN))) top_votes_t_3_mem[ FD_TOP_VOTES_MAX_FOOTPRINT ];
     368             : 
     369             :   /* Copy over the old t-2 top votes into a temporary t-3 buffer.  Copy
     370             :      over the old t-1 top votes to the t-2 top votes.  Reset the
     371             :      existing t-1 top votes to prepare it for insertion. Handle the
     372             :      transition to the next epoch. */
     373           0 :   fd_memcpy( top_votes_t_3_mem, top_votes_t_2, FD_TOP_VOTES_MAX_FOOTPRINT );
     374           0 :   fd_memcpy( top_votes_t_2,     top_votes_t_1, FD_TOP_VOTES_MAX_FOOTPRINT );
     375           0 :   fd_top_votes_init( top_votes_t_1 );
     376           0 :   fd_top_votes_t * top_votes_t_3 = fd_type_pun( top_votes_t_3_mem );
     377             : 
     378           0 :   fd_stake_accum_map_reset( runtime_stack->stakes.stake_accum_map );
     379           0 :   ulong epoch              = bank->f.epoch;
     380           0 :   ulong total_stake        = 0UL;
     381           0 :   ulong total_activating   = 0UL;
     382           0 :   ulong total_deactivating = 0UL;
     383           0 :   ulong staked_accounts    = 0UL;
     384             : 
     385           0 :   fd_stake_accum_t *     stake_accum_pool = runtime_stack->stakes.stake_accum;
     386           0 :   fd_stake_accum_map_t * stake_accum_map  = runtime_stack->stakes.stake_accum_map;
     387             : 
     388             :   /* Accumulate stakes across all delegations for all vote accounts. */
     389           0 :   fd_stake_delegations_iter_t iter_[1];
     390           0 :   for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations );
     391           0 :       !fd_stake_delegations_iter_done( iter );
     392           0 :       fd_stake_delegations_iter_next( iter ) ) {
     393             : 
     394           0 :     fd_stake_delegation_t const * stake_delegation = fd_stake_delegations_iter_ele( iter );
     395             : 
     396           0 :     fd_stake_history_entry_t new_entry = fd_stakes_activating_and_deactivating(
     397           0 :         stake_delegation,
     398           0 :         epoch,
     399           0 :         history,
     400           0 :         new_rate_activation_epoch );
     401           0 :     total_stake        += new_entry.effective;
     402           0 :     total_activating   += new_entry.activating;
     403           0 :     total_deactivating += new_entry.deactivating;
     404             : 
     405           0 :     fd_stake_accum_t * stake_accum = fd_stake_accum_map_ele_query( stake_accum_map, &stake_delegation->vote_account, NULL, stake_accum_pool );
     406           0 :     if( FD_UNLIKELY( !stake_accum ) ) {
     407           0 :       if( FD_UNLIKELY( staked_accounts>=runtime_stack->max_vote_accounts ) ) {
     408           0 :         FD_LOG_ERR(( "invariant violation: staked_accounts >= max_vote_accounts" ));
     409           0 :       }
     410           0 :       stake_accum = &runtime_stack->stakes.stake_accum[ staked_accounts ];
     411           0 :       stake_accum->pubkey = stake_delegation->vote_account;
     412           0 :       stake_accum->stake  = new_entry.effective;
     413           0 :       fd_stake_accum_map_ele_insert( stake_accum_map, stake_accum, stake_accum_pool );
     414           0 :       staked_accounts++;
     415           0 :     } else {
     416           0 :       stake_accum->stake += new_entry.effective;
     417           0 :     }
     418           0 :   }
     419             : 
     420             :   /* Only update total_*_stake at the epoch boundary.  These values
     421             :      are snapshots of the stake totals for the current epoch. */
     422           0 :   bank->f.total_activating_stake   = total_activating;
     423           0 :   bank->f.total_deactivating_stake = total_deactivating;
     424           0 :   bank->f.total_effective_stake    = total_stake;
     425             : 
     426             :   /* Iterate over the valid delegated vote accounts and insert them into
     427             :      the top votes set for the t-1 epoch. */
     428             : 
     429           0 :   for( fd_stake_accum_map_iter_t iter = fd_stake_accum_map_iter_init( stake_accum_map, stake_accum_pool );
     430           0 :        !fd_stake_accum_map_iter_done( iter, stake_accum_map, stake_accum_pool );
     431           0 :        iter = fd_stake_accum_map_iter_next( iter, stake_accum_map, stake_accum_pool ) ) {
     432           0 :     fd_stake_accum_t * stake_accum = fd_stake_accum_map_iter_ele( iter, stake_accum_map, stake_accum_pool );
     433             : 
     434           0 :     fd_pubkey_t node_account_t_1 = {0};
     435           0 :     ulong       stake_t_1        = stake_accum->stake;
     436           0 :     uchar       commission_t_1   = 0;
     437             : 
     438           0 :     fd_accdb_ro_t vote_ro[1];
     439             :     /* Agave's VAT filter also checks lamports against the VoteStateV4
     440             :        rent-exempt minimum. */
     441           0 :     if( FD_UNLIKELY( !fd_accdb_open_ro( accdb, vote_ro, xid, &stake_accum->pubkey ) ) ) {
     442           0 :       continue;
     443           0 :     }
     444           0 :     ulong vote_account_lamports = vote_ro->meta->lamports;
     445           0 :     ulong vote_account_rent_exempt_minimum = fd_rent_exempt_minimum_balance( &bank->f.rent, FD_VOTE_STATE_V4_SZ );
     446           0 :     if( FD_UNLIKELY( vote_account_lamports < vote_account_rent_exempt_minimum ) ) {
     447           0 :       fd_accdb_close_ro( accdb, vote_ro );
     448           0 :       continue;
     449           0 :     }
     450           0 :     if( FD_UNLIKELY( !fd_vsv_is_correct_size_owner_and_init( vote_ro->meta ) ||
     451           0 :                             !fd_vote_account_is_v4_with_bls_pubkey( fd_account_data( vote_ro->meta ), vote_ro->meta->dlen ) ) ) {
     452           0 :       fd_accdb_close_ro( accdb, vote_ro );
     453           0 :       continue;
     454           0 :     }
     455             : 
     456           0 :     FD_TEST( !fd_vote_account_commission( fd_accdb_ref_data_const( vote_ro ), fd_accdb_ref_data_sz( vote_ro ), &commission_t_1 ) );
     457           0 :     FD_TEST( !fd_vote_account_node_pubkey( fd_accdb_ref_data_const( vote_ro ), fd_accdb_ref_data_sz( vote_ro ), &node_account_t_1 ) );
     458             : 
     459           0 :     fd_top_votes_insert( top_votes_t_1, &stake_accum->pubkey, &node_account_t_1, stake_t_1, commission_t_1 );
     460           0 :     fd_accdb_close_ro( accdb, vote_ro );
     461           0 :   }
     462             : 
     463             :   /* Seed status for the t-2 top votes set for clock calculation. */
     464           0 :   uchar __attribute__((aligned(FD_TOP_VOTES_ITER_ALIGN))) top_votes_iter_mem[ FD_TOP_VOTES_ITER_FOOTPRINT ];
     465           0 :   for( fd_top_votes_iter_t * iter = fd_top_votes_iter_init( top_votes_t_2, top_votes_iter_mem );
     466           0 :        !fd_top_votes_iter_done( top_votes_t_2, iter );
     467           0 :        fd_top_votes_iter_next( top_votes_t_2, iter ) ) {
     468           0 :     fd_pubkey_t pubkey;
     469           0 :     uchar       commission_t_2;
     470           0 :     fd_top_votes_iter_ele( top_votes_t_2, iter, &pubkey, NULL, NULL, &commission_t_2, NULL, NULL );
     471             : 
     472           0 :     fd_accdb_ro_t vote_ro[1];
     473           0 :     if( FD_UNLIKELY( !fd_accdb_open_ro( accdb, vote_ro, xid, &pubkey ) ) ) {
     474           0 :       fd_top_votes_invalidate( top_votes_t_2, &pubkey );
     475           0 :       continue;
     476           0 :     }
     477           0 :     if( FD_UNLIKELY( !fd_vsv_is_correct_size_owner_and_init( vote_ro->meta ) ) ) {
     478           0 :       fd_top_votes_invalidate( top_votes_t_2, &pubkey );
     479           0 :       fd_accdb_close_ro( accdb, vote_ro );
     480           0 :       continue;
     481           0 :     }
     482             : 
     483           0 :     fd_vote_block_timestamp_t last_vote;
     484           0 :     FD_TEST( !fd_vote_account_last_timestamp( fd_account_data( vote_ro->meta ), vote_ro->meta->dlen, &last_vote ) );
     485           0 :     fd_top_votes_update( top_votes_t_2, &pubkey, last_vote.slot, last_vote.timestamp );
     486           0 :     fd_accdb_close_ro( accdb, vote_ro );
     487           0 :   }
     488             : 
     489             :   /* Populate the vote rewards map with the final set of filtered vote
     490             :      accounts. */
     491           0 :   fd_vote_rewards_map_t * vote_reward_map = runtime_stack->stakes.vote_map;
     492           0 :   fd_vote_rewards_map_reset( vote_reward_map );
     493           0 :   ulong vote_reward_cnt = 0UL;
     494             : 
     495             :   /* If VAT feature has just been activated, we want to reference the
     496             :      t-2/t-3 commissions from the vote stakes and not the top votes. */
     497           0 :   ulong vat_epoch  = fd_slot_to_epoch( &bank->f.epoch_schedule, bank->f.features.validator_admission_ticket, NULL );
     498           0 :   int   vat_in_t_2 = bank->f.epoch>vat_epoch;
     499           0 :   int   vat_in_t_3 = fd_ulong_sat_sub(bank->f.epoch, 1UL )>vat_epoch;
     500             : 
     501           0 :   ushort             parent_idx  = bank->vote_stakes_fork_id;
     502           0 :   fd_vote_stakes_t * vote_stakes = fd_bank_vote_stakes( bank );
     503             : 
     504             :   /* Populate the vote rewards map with the final set of filtered vote
     505             :      accounts for the t-1 epoch. */
     506           0 :   bank->f.total_epoch_stake = 0UL;
     507           0 :   for( fd_top_votes_iter_t * iter = fd_top_votes_iter_init( top_votes_t_1, top_votes_iter_mem );
     508           0 :        !fd_top_votes_iter_done( top_votes_t_1, iter );
     509           0 :        fd_top_votes_iter_next( top_votes_t_1, iter ) ) {
     510           0 :     fd_pubkey_t pubkey;
     511           0 :     ulong       stake;
     512           0 :     uchar       commission_t_1 = 0;
     513           0 :     fd_top_votes_iter_ele( top_votes_t_1, iter, &pubkey, NULL, &stake, &commission_t_1, NULL, NULL );
     514             : 
     515           0 :     int   exists_t_3 = 0;
     516           0 :     uchar commission_t_3 = 0;
     517           0 :     if( FD_LIKELY( vat_in_t_3 ) ) {
     518           0 :       exists_t_3 = fd_top_votes_query( top_votes_t_3, &pubkey, NULL, NULL, NULL, NULL, &commission_t_3 );
     519           0 :     } else {
     520           0 :       exists_t_3 = fd_vote_stakes_query_t_2( vote_stakes, parent_idx, &pubkey, NULL, NULL, &commission_t_3 );
     521           0 :     }
     522             : 
     523           0 :     int   exists_t_2     = 0;
     524           0 :     uchar commission_t_2 = 0;
     525           0 :     if( FD_LIKELY( vat_in_t_2 ) ) {
     526           0 :       exists_t_2 = fd_top_votes_query( top_votes_t_2, &pubkey, NULL, NULL, NULL, NULL, &commission_t_2 );
     527           0 :     } else {
     528           0 :       exists_t_2 = fd_vote_stakes_query_t_1( vote_stakes, parent_idx, &pubkey, NULL, NULL, &commission_t_2 );
     529           0 :     }
     530             : 
     531           0 :     fd_vote_rewards_t * vote_ele = &runtime_stack->stakes.vote_ele[ vote_reward_cnt ];
     532           0 :     vote_ele->pubkey             = pubkey;
     533           0 :     vote_ele->vote_rewards       = 0UL;
     534           0 :     if( FD_FEATURE_ACTIVE_BANK( bank, delay_commission_updates ) ) {
     535           0 :       vote_ele->commission = exists_t_3 ? commission_t_3 : (exists_t_2 ? commission_t_2 : commission_t_1);
     536           0 :     } else {
     537           0 :       vote_ele->commission = commission_t_1;
     538           0 :     }
     539             : 
     540           0 :     fd_accdb_ro_t vote_ro[1];
     541           0 :     FD_TEST( fd_accdb_open_ro( accdb, vote_ro, xid, &pubkey ) );
     542           0 :     fd_epoch_credits_t * epoch_credits = &fd_bank_epoch_credits( bank )[ vote_reward_cnt ];
     543           0 :     get_vote_credits( fd_accdb_ref_data_const( vote_ro ), fd_accdb_ref_data_sz( vote_ro ), epoch_credits );
     544           0 :     fd_accdb_close_ro( accdb, vote_ro );
     545             : 
     546           0 :     fd_vote_rewards_map_ele_insert( vote_reward_map, vote_ele, runtime_stack->stakes.vote_ele );
     547           0 :     vote_reward_cnt++;
     548           0 :     bank->f.total_epoch_stake += stake;
     549           0 :   }
     550           0 :   *fd_bank_epoch_credits_len( bank ) = vote_reward_cnt;
     551           0 : }
     552             : 
     553             : static void
     554             : fd_refresh_vote_accounts_no_vat( fd_bank_t *                    bank,
     555             :                                  fd_accdb_user_t *              accdb,
     556             :                                  fd_funk_txn_xid_t const *      xid,
     557             :                                  fd_runtime_stack_t *           runtime_stack,
     558             :                                  fd_stake_delegations_t const * stake_delegations,
     559             :                                  fd_stake_history_t const *     history,
     560         129 :                                  ulong *                        new_rate_activation_epoch ) {
     561         129 :   fd_vote_rewards_map_t * vote_reward_map = runtime_stack->stakes.vote_map;
     562         129 :   fd_vote_rewards_map_reset( vote_reward_map );
     563         129 :   ulong vote_reward_cnt = 0UL;
     564             : 
     565             :   /* First accumulate stakes across all delegations for all vote
     566             :      accounts.  At this point, don't care if they are valid accounts or
     567             :      if they will be inserted into the top votes set. */
     568             : 
     569         129 :   fd_stake_accum_t *     stake_accum_pool = runtime_stack->stakes.stake_accum;
     570         129 :   fd_stake_accum_map_t * stake_accum_map  = runtime_stack->stakes.stake_accum_map;
     571             : 
     572         129 :   ushort parent_idx = bank->vote_stakes_fork_id;
     573             : 
     574         129 :   fd_stake_accum_map_reset( runtime_stack->stakes.stake_accum_map );
     575         129 :   ulong epoch              = bank->f.epoch;
     576         129 :   ulong total_stake        = 0UL;
     577         129 :   ulong total_activating   = 0UL;
     578         129 :   ulong total_deactivating = 0UL;
     579         129 :   ulong staked_accounts    = 0UL;
     580             : 
     581             :   /* Seed stake_accum_map with all vote accounts from the parent fork
     582             :      with zero stake. The delegation loop below will update the stake
     583             :      for any account that has active delegations.  This needs to be done
     584             :      because zero-staked, active vote accounts have their historical
     585             :      commission tracked for payouts. */
     586             : 
     587         129 :   fd_vote_stakes_t * vs = fd_bank_vote_stakes( bank );
     588         129 :   uchar __attribute__((aligned(FD_VOTE_STAKES_ITER_ALIGN))) iter_mem_vs[ FD_VOTE_STAKES_ITER_FOOTPRINT ];
     589         129 :   for( fd_vote_stakes_iter_t * vs_iter = fd_vote_stakes_fork_iter_init( vs, parent_idx, iter_mem_vs );
     590         264 :         !fd_vote_stakes_fork_iter_done( vs, parent_idx, vs_iter );
     591         135 :         fd_vote_stakes_fork_iter_next( vs, parent_idx, vs_iter ) ) {
     592         135 :     fd_pubkey_t vs_pubkey;
     593         135 :     fd_vote_stakes_fork_iter_ele( vs, parent_idx, vs_iter, &vs_pubkey, NULL, NULL, NULL, NULL, NULL, NULL );
     594         135 :     if( FD_UNLIKELY( staked_accounts>=runtime_stack->max_vote_accounts ) ) {
     595           0 :       FD_LOG_ERR(( "invariant violation: staked_accounts >= max_vote_accounts" ));
     596           0 :     }
     597         135 :     fd_stake_accum_t * sa = &runtime_stack->stakes.stake_accum[ staked_accounts ];
     598         135 :     if( !fd_stake_accum_map_ele_query( stake_accum_map, &vs_pubkey, NULL, stake_accum_pool ) ) {
     599         135 :       sa->pubkey = vs_pubkey;
     600         135 :       sa->stake  = 0UL;
     601         135 :       fd_stake_accum_map_ele_insert( stake_accum_map, sa, stake_accum_pool );
     602         135 :       staked_accounts++;
     603         135 :     }
     604         135 :   }
     605         129 :   fd_vote_stakes_fork_iter_fini( vs );
     606             : 
     607             :   /* Add any pubkeys visible via new_votes that were not already seeded
     608             :      from the parent vote_stakes set. Some iterator entries may be
     609             :      tombstones: that is harmless here because a pubkey absent from both
     610             :      the current accdb view and the parent vote_stakes view is filtered
     611             :      out before insertion into the child vote_stakes.  New vote accounts
     612             :      with zero staked must be tracked for historical commission
     613             :      lookups. */
     614             : 
     615         129 :   fd_new_votes_t * new_votes = fd_bank_new_votes( bank );
     616         129 :   ushort           fork_indices[ FD_RUNTIME_MAX_FORK_CNT ];
     617         129 :   ulong            forks_cnt = fd_banks_new_votes_fork_indices( bank, fork_indices );
     618             : 
     619         129 :   uchar __attribute__((aligned(FD_NEW_VOTES_ITER_ALIGN))) iter_mem[ FD_NEW_VOTES_ITER_FOOTPRINT ];
     620         129 :   fd_new_votes_iter_t * iter = fd_new_votes_iter_init( new_votes, fork_indices, forks_cnt, iter_mem );
     621         129 :   for( ; !fd_new_votes_iter_done( iter ); fd_new_votes_iter_next( iter ) ) {
     622           0 :     int                 is_tombstone = 0;
     623           0 :     fd_pubkey_t const * pubkey       = fd_new_votes_iter_ele( iter, &is_tombstone );
     624           0 :     if( FD_UNLIKELY( is_tombstone ) ) continue;
     625             : 
     626           0 :     if( FD_LIKELY( !fd_stake_accum_map_ele_query( stake_accum_map, pubkey, NULL, stake_accum_pool ) ) ) {
     627           0 :       fd_stake_accum_t * sa = &runtime_stack->stakes.stake_accum[ staked_accounts ];
     628           0 :       sa->pubkey = *pubkey;
     629           0 :       sa->stake  = 0UL;
     630           0 :       fd_stake_accum_map_ele_insert( stake_accum_map, sa, stake_accum_pool );
     631           0 :       staked_accounts++;
     632           0 :     }
     633           0 :   }
     634         129 :   fd_new_votes_iter_fini( iter );
     635             : 
     636             :   /* Now accumulate vote stakes for all stake delegations. */
     637             : 
     638         129 :   fd_stake_delegations_iter_t iter_[1];
     639         129 :   for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations );
     640         264 :        !fd_stake_delegations_iter_done( iter );
     641         135 :        fd_stake_delegations_iter_next( iter ) ) {
     642             : 
     643         135 :     fd_stake_delegation_t const * stake_delegation = fd_stake_delegations_iter_ele( iter );
     644             : 
     645         135 :     fd_stake_history_entry_t new_entry = fd_stakes_activating_and_deactivating(
     646         135 :         stake_delegation,
     647         135 :         epoch,
     648         135 :         history,
     649         135 :         new_rate_activation_epoch );
     650         135 :     total_stake        += new_entry.effective;
     651         135 :     total_activating   += new_entry.activating;
     652         135 :     total_deactivating += new_entry.deactivating;
     653             : 
     654         135 :     fd_stake_accum_t * stake_accum = fd_stake_accum_map_ele_query( stake_accum_map, &stake_delegation->vote_account, NULL, stake_accum_pool );
     655         135 :     if( FD_UNLIKELY( !stake_accum ) ) {
     656           0 :       if( FD_UNLIKELY( staked_accounts>=runtime_stack->max_vote_accounts ) ) {
     657           0 :         FD_LOG_ERR(( "invariant violation: staked_accounts >= max_vote_accounts" ));
     658           0 :       }
     659           0 :       stake_accum = &runtime_stack->stakes.stake_accum[ staked_accounts ];
     660           0 :       stake_accum->pubkey = stake_delegation->vote_account;
     661           0 :       stake_accum->stake  = new_entry.effective;
     662           0 :       fd_stake_accum_map_ele_insert( stake_accum_map, stake_accum, stake_accum_pool );
     663           0 :       staked_accounts++;
     664         135 :     } else {
     665         135 :       stake_accum->stake += new_entry.effective;
     666         135 :     }
     667         135 :   }
     668             : 
     669             :   /* Only update total_*_stake at the epoch boundary.  These values
     670             :      are snapshots of the stake totals for the current epoch. */
     671         129 :   bank->f.total_activating_stake   = total_activating;
     672         129 :   bank->f.total_deactivating_stake = total_deactivating;
     673         129 :   bank->f.total_effective_stake    = total_stake;
     674             : 
     675             :   /* Copy the top votes set for the t-1 epoch into the t-2 epoch now
     676             :      that the epoch boundary is being crossed.  Reset the existing t-1
     677             :      top votes set to prepare it for insertion.  Refresh the states of
     678             :      the t-2 top votes set: figure out if the account still exists and
     679             :      what the last vote timestamp and slot are. */
     680             : 
     681         129 :   fd_top_votes_t * top_votes_t_1 = fd_bank_top_votes_t_1_modify( bank );
     682         129 :   fd_top_votes_t * top_votes_t_2 = fd_bank_top_votes_t_2_modify( bank );
     683         129 :   fd_memcpy( top_votes_t_2, top_votes_t_1, FD_TOP_VOTES_MAX_FOOTPRINT );
     684         129 :   fd_top_votes_init( top_votes_t_1 );
     685             : 
     686         129 :   uchar __attribute__((aligned(FD_TOP_VOTES_ITER_ALIGN))) top_votes_iter_mem[ FD_TOP_VOTES_ITER_FOOTPRINT ];
     687         129 :   for( fd_top_votes_iter_t * iter = fd_top_votes_iter_init( top_votes_t_2, top_votes_iter_mem );
     688         261 :        !fd_top_votes_iter_done( top_votes_t_2, iter );
     689         132 :        fd_top_votes_iter_next( top_votes_t_2, iter ) ) {
     690         132 :     fd_pubkey_t pubkey;
     691         132 :     uchar       commission_t_2;
     692         132 :     fd_top_votes_iter_ele( top_votes_t_2, iter, &pubkey, NULL, NULL, &commission_t_2, NULL, NULL );
     693             : 
     694         132 :     fd_accdb_ro_t vote_ro[1];
     695         132 :     if( FD_UNLIKELY( !fd_accdb_open_ro( accdb, vote_ro, xid, &pubkey ) ) ) {
     696           0 :       fd_top_votes_invalidate( top_votes_t_2, &pubkey );
     697           0 :       continue;
     698           0 :     }
     699         132 :     if( FD_UNLIKELY( !fd_vsv_is_correct_size_owner_and_init( vote_ro->meta ) ) ) {
     700           0 :       fd_top_votes_invalidate( top_votes_t_2, &pubkey );
     701           0 :       fd_accdb_close_ro( accdb, vote_ro );
     702           0 :       continue;
     703           0 :     }
     704             : 
     705         132 :     fd_vote_block_timestamp_t last_vote;
     706         132 :     FD_TEST( !fd_vote_account_last_timestamp( fd_account_data( vote_ro->meta ), vote_ro->meta->dlen, &last_vote ) );
     707         132 :     fd_top_votes_update( top_votes_t_2, &pubkey, last_vote.slot, last_vote.timestamp );
     708         132 :     fd_accdb_close_ro( accdb, vote_ro );
     709         132 :   }
     710             : 
     711             :   /* Now for each staked vote account, figure out if it is a valid
     712             :      account and insert into the vote stakes (an account can not exist
     713             :      but still be inserted into the vote stakes if it existed in the
     714             :      previous epoch or vice versa).  The only condition an account is
     715             :      not inserted into the vote stakes is if it didn't exist at the end
     716             :      of the t-2 epoch and the end of the t-1 epoch assuming we are
     717             :      transitioning into epoch t. */
     718             : 
     719         129 :   fd_vote_stakes_t * vote_stakes = fd_bank_vote_stakes( bank );
     720         129 :   ushort             child_idx   = fd_vote_stakes_new_child( vote_stakes );
     721         129 :   bank->vote_stakes_fork_id      = child_idx;
     722             : 
     723         129 :   bank->f.total_epoch_stake = 0UL;
     724         129 :   for( fd_stake_accum_map_iter_t iter = fd_stake_accum_map_iter_init( stake_accum_map, stake_accum_pool );
     725         264 :        !fd_stake_accum_map_iter_done( iter, stake_accum_map, stake_accum_pool );
     726         135 :        iter = fd_stake_accum_map_iter_next( iter, stake_accum_map, stake_accum_pool ) ) {
     727         135 :     fd_stake_accum_t * stake_accum = fd_stake_accum_map_iter_ele( iter, stake_accum_map, stake_accum_pool );
     728             : 
     729         135 :     fd_pubkey_t node_account_t_2 = {0};
     730         135 :     ulong       stake_t_2        = 0UL;
     731         135 :     uchar       commission_t_2   = 0;
     732         135 :     int         exists_t_2       = fd_vote_stakes_query_t_1( vote_stakes, parent_idx, &stake_accum->pubkey, &stake_t_2, &node_account_t_2, &commission_t_2 );
     733             : 
     734         135 :     fd_pubkey_t node_account_t_1 = {0};
     735         135 :     ulong       stake_t_1        = 0UL;
     736         135 :     uchar       commission_t_1   = 0;
     737             : 
     738         135 :     fd_accdb_ro_t vote_ro[1];
     739         135 :     int exists_t_1 = 1;
     740         135 :     if( FD_UNLIKELY( !fd_accdb_open_ro( accdb, vote_ro, xid, &stake_accum->pubkey ) ) ) {
     741           0 :       exists_t_1 = 0;
     742         135 :     } else if( FD_UNLIKELY( !fd_vsv_is_correct_size_owner_and_init( vote_ro->meta ) ) ) {
     743           0 :       exists_t_1 = 0;
     744           0 :       fd_accdb_close_ro( accdb, vote_ro );
     745         135 :     } else {
     746             : 
     747         135 :       FD_TEST( !fd_vote_account_commission( fd_accdb_ref_data_const( vote_ro ), fd_accdb_ref_data_sz( vote_ro ), &commission_t_1 ) );
     748         135 :       FD_TEST( !fd_vote_account_node_pubkey( fd_accdb_ref_data_const( vote_ro ), fd_accdb_ref_data_sz( vote_ro ), &node_account_t_1 ) );
     749             : 
     750         135 :       stake_t_1 = stake_accum->stake;
     751         135 :       bank->f.total_epoch_stake += stake_t_1;
     752             : 
     753         135 :       fd_pubkey_t node_account_t_3 = {0};
     754         135 :       ulong       stake_t_3        = 0UL;
     755         135 :       uchar       commission_t_3   = 0;
     756         135 :       int         exists_t_3       = fd_vote_stakes_query_t_2( vote_stakes, parent_idx, &stake_accum->pubkey, &stake_t_3, &node_account_t_3, &commission_t_3 );
     757             : 
     758         135 :       fd_epoch_credits_t * epoch_credits = &fd_bank_epoch_credits( bank )[ vote_reward_cnt ];
     759         135 :       get_vote_credits( fd_accdb_ref_data_const( vote_ro ), fd_accdb_ref_data_sz( vote_ro ), epoch_credits );
     760         135 :       fd_vote_rewards_t * vote_ele = &runtime_stack->stakes.vote_ele[ vote_reward_cnt ];
     761         135 :       vote_ele->pubkey             = stake_accum->pubkey;
     762         135 :       vote_ele->vote_rewards       = 0UL;
     763         135 :       if( FD_FEATURE_ACTIVE_BANK( bank, delay_commission_updates ) ) {
     764           0 :         vote_ele->commission = exists_t_3 ? commission_t_3 : (exists_t_2 ? commission_t_2 : commission_t_1);
     765         135 :       } else {
     766         135 :         vote_ele->commission = commission_t_1;
     767         135 :       }
     768         135 :       fd_vote_rewards_map_ele_insert( vote_reward_map, vote_ele, runtime_stack->stakes.vote_ele );
     769         135 :       vote_reward_cnt++;
     770             : 
     771         135 :       fd_top_votes_insert( top_votes_t_1, &stake_accum->pubkey, &node_account_t_1, stake_t_1, commission_t_1 );
     772         135 :       fd_accdb_close_ro( accdb, vote_ro );
     773         135 :     }
     774             : 
     775         135 :     if( FD_UNLIKELY( !exists_t_1 && !exists_t_2 ) ) continue;
     776         135 :     fd_vote_stakes_insert(
     777         135 :         vote_stakes, child_idx, &stake_accum->pubkey,
     778         135 :         &node_account_t_1, &node_account_t_2,
     779         135 :         stake_t_1, stake_t_2,
     780         135 :         commission_t_1, commission_t_2,
     781         135 :         (uchar)exists_t_1, (uchar)exists_t_2,
     782         135 :         bank->f.epoch );
     783         135 :   }
     784         129 :   *fd_bank_epoch_credits_len( bank ) = vote_reward_cnt;
     785         129 : }
     786             : 
     787             : /* We need to update the amount of stake that each vote account has for
     788             :    the given epoch.  This can only be done after the stake history
     789             :    sysvar has been updated.  We also cache the stakes for each of the
     790             :    vote accounts for the previous epoch.
     791             : 
     792             :    https://github.com/anza-xyz/agave/blob/v3.0.4/runtime/src/stakes.rs#L471 */
     793             : void
     794             : fd_refresh_vote_accounts( fd_bank_t *                    bank,
     795             :                           fd_accdb_user_t *              accdb,
     796             :                           fd_funk_txn_xid_t const *      xid,
     797             :                           fd_runtime_stack_t *           runtime_stack,
     798             :                           fd_stake_delegations_t const * stake_delegations,
     799             :                           fd_stake_history_t const *     history,
     800         129 :                           ulong *                        new_rate_activation_epoch ) {
     801             :   /* If validator_admission_ticket is enabled, the top 2000 vote
     802             :      accounts for every epoch (the agave epoch stakes), are stored in
     803             :      the top votes set.  If the feature is not active, there is no
     804             :      stake-based filtering on the vote accounts that are eligible for
     805             :      receving rewards/being included in the leader schedule computation.
     806             :      Once the feature is active, only the top vote accounts will be
     807             :      tracked for historical stake/node_account/commission lookups.
     808             :      The non vat code path uses the vote stakes data structure as it
     809             :      considers all vote/stake accounts. */
     810         129 :   if( FD_FEATURE_ACTIVE_BANK( bank, validator_admission_ticket ) ) {
     811           0 :     fd_refresh_vote_accounts_vat( bank, accdb, xid, runtime_stack, stake_delegations, history, new_rate_activation_epoch );
     812         129 :   } else {
     813         129 :     fd_refresh_vote_accounts_no_vat( bank, accdb, xid, runtime_stack, stake_delegations, history, new_rate_activation_epoch );
     814         129 :   }
     815         129 : }
     816             : 
     817             : /* https://github.com/anza-xyz/agave/blob/v3.0.4/runtime/src/stakes.rs#L280 */
     818             : void
     819             : fd_stakes_activate_epoch( fd_bank_t *                    bank,
     820             :                           fd_runtime_stack_t *           runtime_stack,
     821             :                           fd_accdb_user_t *              accdb,
     822             :                           fd_funk_txn_xid_t const *      xid,
     823             :                           fd_capture_ctx_t *             capture_ctx,
     824             :                           fd_stake_delegations_t const * stake_delegations,
     825         129 :                           ulong *                        new_rate_activation_epoch ) {
     826             : 
     827             :   /* We can update our stake history sysvar based on the bank stake values.
     828             :      Afterward, we can refresh the stake values for the vote accounts. */
     829             : 
     830         129 :   fd_stake_history_entry_t elem = {
     831         129 :     .epoch        = bank->f.epoch,
     832         129 :     .effective    = stake_delegations->effective_stake,
     833         129 :     .activating   = stake_delegations->activating_stake,
     834         129 :     .deactivating = stake_delegations->deactivating_stake,
     835         129 :   };
     836         129 :   fd_sysvar_stake_history_update( bank, accdb, xid, capture_ctx, &elem );
     837             : 
     838         129 :   fd_accdb_ro_t sh_ro[1];
     839         129 :   if( FD_UNLIKELY( !fd_accdb_open_ro( accdb, sh_ro, xid, &fd_sysvar_stake_history_id ) ) ) {
     840           0 :     FD_LOG_ERR(( "StakeHistory sysvar is missing" ));
     841           0 :   }
     842         129 :   fd_stake_history_t stake_history[1];
     843         129 :   if( FD_UNLIKELY( !fd_sysvar_stake_history_view( stake_history, fd_accdb_ref_data_const( sh_ro ), fd_accdb_ref_data_sz( sh_ro ) ) ) ) {
     844           0 :     FD_LOG_HEXDUMP_ERR(( "Invalid StakeHistory sysvar", fd_accdb_ref_data_const( sh_ro ), fd_accdb_ref_data_sz( sh_ro ) ));
     845           0 :   }
     846             : 
     847             :   /* Now increment the epoch and recompute the stakes for the vote
     848             :      accounts for the new epoch value. */
     849             : 
     850         129 :   bank->f.epoch = fd_slot_to_epoch( &bank->f.epoch_schedule, bank->f.slot, NULL );
     851             : 
     852         129 :   fd_refresh_vote_accounts( bank,
     853         129 :                             accdb,
     854         129 :                             xid,
     855         129 :                             runtime_stack,
     856         129 :                             stake_delegations,
     857         129 :                             stake_history,
     858         129 :                             new_rate_activation_epoch );
     859             : 
     860         129 :   fd_accdb_close_ro( accdb, sh_ro );
     861         129 : }
     862             : 
     863             : 
     864             : void
     865             : fd_stakes_update_stake_delegation( fd_pubkey_t const *       pubkey,
     866             :                                    fd_account_meta_t const * meta,
     867           3 :                                    fd_bank_t *               bank ) {
     868             : 
     869           3 :   fd_stake_delegations_t * stake_delegations = fd_bank_stake_delegations_modify( bank );
     870             : 
     871             :   /* fd_stakes_get_state returns NULL for closed/invalid accounts. */
     872           3 :   fd_stake_state_t const * stake_state = fd_stakes_get_state( meta );
     873           3 :   if( FD_LIKELY( stake_state != NULL &&
     874           3 :                  stake_state->stake_type == FD_STAKE_STATE_STAKE &&
     875           3 :                  stake_state->stake.stake.delegation.stake != 0UL ) ) {
     876             : 
     877           0 :     ulong new_stake = stake_state->stake.stake.delegation.stake;
     878           0 :     fd_stake_delegations_fork_update( stake_delegations, bank->stake_delegations_fork_id, pubkey,
     879           0 :                                       &stake_state->stake.stake.delegation.voter_pubkey,
     880           0 :                                       new_stake,
     881           0 :                                       stake_state->stake.stake.delegation.activation_epoch,
     882           0 :                                       stake_state->stake.stake.delegation.deactivation_epoch,
     883           0 :                                       stake_state->stake.stake.credits_observed,
     884           0 :                                       fd_stake_warmup_cooldown_rate( bank->f.epoch, &bank->f.warmup_cooldown_rate_epoch ) );
     885             : 
     886           3 :   } else {
     887           3 :     fd_stake_delegations_fork_remove( stake_delegations, bank->stake_delegations_fork_id, pubkey );
     888           3 :   }
     889           3 : }

Generated by: LCOV version 1.14