LCOV - code coverage report
Current view: top level - flamenco/stakes - fd_stakes.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 181 0.0 %
Date: 2025-09-18 04:41:32 Functions: 0 10 0.0 %

          Line data    Source code
       1             : #include "fd_stakes.h"
       2             : #include "../runtime/fd_acc_mgr.h"
       3             : #include "../runtime/fd_system_ids.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             : #include "fd_stake_delegations.h"
       8             : 
       9             : ulong
      10             : fd_stake_weights_by_node( fd_vote_states_t const * vote_states,
      11           0 :                           fd_vote_stake_weight_t * weights ) {
      12             : 
      13           0 :   ulong weights_cnt = 0;
      14           0 :   fd_vote_states_iter_t iter_[1];
      15           0 :   for( fd_vote_states_iter_t * iter = fd_vote_states_iter_init( iter_, vote_states ); !fd_vote_states_iter_done( iter ); fd_vote_states_iter_next( iter ) ) {
      16           0 :     fd_vote_state_ele_t const * vote_state = fd_vote_states_iter_ele( iter );
      17           0 :     if( FD_UNLIKELY( !vote_state->stake ) ) continue;
      18             : 
      19           0 :     fd_memcpy( weights[ weights_cnt ].vote_key.uc, &vote_state->vote_account, sizeof(fd_pubkey_t) );
      20           0 :     fd_memcpy( weights[ weights_cnt ].id_key.uc, &vote_state->node_account, sizeof(fd_pubkey_t) );
      21           0 :     weights[ weights_cnt ].stake = vote_state->stake;
      22           0 :     weights_cnt++;
      23           0 :   }
      24           0 :   sort_vote_weights_by_stake_vote_inplace( weights, weights_cnt );
      25           0 :   return weights_cnt;
      26           0 : }
      27             : 
      28             : static void
      29             : compute_stake_delegations( fd_bank_t *                    bank,
      30             :                            fd_stake_delegations_t const * stake_delegations,
      31             :                            ulong const                    epoch,
      32             :                            fd_stake_history_t const *     history,
      33           0 :                            ulong *                        new_rate_activation_epoch ) {
      34             : 
      35           0 :   ulong total_stake = 0UL;
      36             : 
      37           0 :   fd_vote_states_t * vote_states = fd_bank_vote_states_locking_modify( bank );
      38           0 :   if( FD_UNLIKELY( !vote_states ) ) {
      39           0 :     FD_LOG_CRIT(( "vote_states is NULL" ));
      40           0 :   }
      41             : 
      42             :   /* Reset the vote stakes so we can re-compute them based on the most
      43             :      current stake delegation values. */
      44           0 :   fd_vote_states_reset_stakes( vote_states );
      45             : 
      46           0 :   fd_stake_delegations_iter_t iter_[1];
      47           0 :   for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations );
      48           0 :        !fd_stake_delegations_iter_done( iter );
      49           0 :        fd_stake_delegations_iter_next( iter ) ) {
      50           0 :     fd_stake_delegation_t const * stake_delegation = fd_stake_delegations_iter_ele( iter );
      51             : 
      52             :     // Skip any delegations that are not in the delegation pool
      53             : 
      54           0 :     fd_delegation_t delegation = {
      55           0 :       .voter_pubkey         = stake_delegation->vote_account,
      56           0 :       .stake                = stake_delegation->stake,
      57           0 :       .deactivation_epoch   = stake_delegation->deactivation_epoch,
      58           0 :       .activation_epoch     = stake_delegation->activation_epoch,
      59           0 :       .warmup_cooldown_rate = stake_delegation->warmup_cooldown_rate,
      60           0 :     };
      61             : 
      62           0 :     fd_stake_history_entry_t new_entry = fd_stake_activating_and_deactivating(
      63           0 :         &delegation,
      64           0 :         epoch,
      65           0 :         history,
      66           0 :         new_rate_activation_epoch );
      67             : 
      68           0 :     fd_vote_state_ele_t * vote_state = fd_vote_states_query( vote_states, &stake_delegation->vote_account );
      69           0 :     if( FD_LIKELY( vote_state ) ) {
      70           0 :       total_stake       += new_entry.effective;
      71           0 :       vote_state->stake += new_entry.effective;
      72           0 :     }
      73           0 :   }
      74             : 
      75           0 :   fd_bank_total_epoch_stake_set( bank, total_stake );
      76             : 
      77           0 :   fd_bank_vote_states_end_locking_modify( bank );
      78           0 : }
      79             : 
      80             : /* Refresh vote accounts.
      81             : 
      82             :    This updates the epoch bank stakes vote_accounts cache - that is, the total amount
      83             :    of delegated stake each vote account has, using the current delegation values from inside each
      84             :    stake account. Contrary to the Agave equivalent, it also merges the stakes cache vote accounts with the
      85             :    new vote account keys from this epoch.
      86             : 
      87             :    https://github.com/solana-labs/solana/blob/c091fd3da8014c0ef83b626318018f238f506435/runtime/src/stakes.rs#L562 */
      88             : void
      89             : fd_refresh_vote_accounts( fd_exec_slot_ctx_t *           slot_ctx,
      90             :                           fd_stake_delegations_t const * stake_delegations,
      91             :                           fd_stake_history_t const *     history,
      92           0 :                           ulong *                        new_rate_activation_epoch ) {
      93             : 
      94           0 :   compute_stake_delegations(
      95           0 :       slot_ctx->bank,
      96           0 :       stake_delegations,
      97           0 :       fd_bank_epoch_get( slot_ctx->bank ),
      98           0 :       history,
      99           0 :       new_rate_activation_epoch );
     100           0 : }
     101             : 
     102             : static void
     103             : accumulate_stake_cache_delegations( fd_stake_delegations_t const * stake_delegations,
     104             :                                     fd_stake_history_t const *     history,
     105             :                                     ulong *                        new_rate_activation_epoch,
     106             :                                     fd_stake_history_entry_t *     accumulator,
     107           0 :                                     ulong                          epoch ) {
     108             : 
     109           0 :   ulong effective    = 0UL;
     110           0 :   ulong activating   = 0UL;
     111           0 :   ulong deactivating = 0UL;
     112             : 
     113           0 :   fd_stake_delegations_iter_t iter_[1];
     114           0 :   for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations );
     115           0 :        !fd_stake_delegations_iter_done( iter );
     116           0 :        fd_stake_delegations_iter_next( iter ) ) {
     117           0 :     fd_stake_delegation_t const * stake_delegation = fd_stake_delegations_iter_ele( iter );
     118             : 
     119           0 :     fd_delegation_t delegation = {
     120           0 :       .voter_pubkey         = stake_delegation->vote_account,
     121           0 :       .stake                = stake_delegation->stake,
     122           0 :       .activation_epoch     = stake_delegation->activation_epoch,
     123           0 :       .deactivation_epoch   = stake_delegation->deactivation_epoch,
     124           0 :       .warmup_cooldown_rate = stake_delegation->warmup_cooldown_rate,
     125           0 :     };
     126             : 
     127           0 :     fd_stake_history_entry_t new_entry = fd_stake_activating_and_deactivating(
     128           0 :         &delegation,
     129           0 :         epoch,
     130           0 :         history,
     131           0 :         new_rate_activation_epoch );
     132           0 :     effective    += new_entry.effective;
     133           0 :     activating   += new_entry.activating;
     134           0 :     deactivating += new_entry.deactivating;
     135           0 :   }
     136             : 
     137           0 :   accumulator->effective    += effective;
     138           0 :   accumulator->activating   += activating;
     139           0 :   accumulator->deactivating += deactivating;
     140             : 
     141           0 : }
     142             : 
     143             : /* Accumulates information about epoch stakes into `temp_info`, which is a temporary cache
     144             :    used to save intermediate state about stake and vote accounts to avoid them from having to
     145             :    be recomputed on every access, especially at the epoch boundary. Also collects stats in `accumulator` */
     146             : void
     147             : fd_accumulate_stake_infos( ulong                          epoch,
     148             :                            fd_stake_delegations_t const * stake_delegations,
     149             :                            fd_stake_history_t const *     history,
     150             :                            ulong *                        new_rate_activation_epoch,
     151           0 :                            fd_stake_history_entry_t *     accumulator ) {
     152             : 
     153           0 :   accumulate_stake_cache_delegations(
     154           0 :       stake_delegations,
     155           0 :       history,
     156           0 :       new_rate_activation_epoch,
     157           0 :       accumulator,
     158           0 :       epoch );
     159             : 
     160           0 : }
     161             : 
     162             : /* https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/runtime/src/stakes.rs#L169 */
     163             : void
     164             : fd_stakes_activate_epoch( fd_exec_slot_ctx_t *           slot_ctx,
     165             :                           fd_stake_delegations_t const * stake_delegations,
     166             :                           ulong *                        new_rate_activation_epoch,
     167           0 :                           fd_spad_t *                    runtime_spad ) {
     168             : 
     169             :   /* Current stake delegations: list of all current delegations in stake_delegations
     170             :      https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/runtime/src/stakes.rs#L180 */
     171             :   /* Add a new entry to the Stake History sysvar for the previous epoch
     172             :      https://github.com/solana-labs/solana/blob/88aeaa82a856fc807234e7da0b31b89f2dc0e091/runtime/src/stakes.rs#L181-L192 */
     173             : 
     174           0 :   fd_stake_history_t const * history = fd_sysvar_stake_history_read( slot_ctx->funk, slot_ctx->funk_txn, runtime_spad );
     175           0 :   if( FD_UNLIKELY( !history ) ) FD_LOG_ERR(( "StakeHistory sysvar is missing from sysvar cache" ));
     176             : 
     177           0 :   fd_stake_history_entry_t accumulator = {
     178           0 :     .effective    = 0UL,
     179           0 :     .activating   = 0UL,
     180           0 :     .deactivating = 0UL
     181           0 :   };
     182             : 
     183             :   /* Accumulate stats for stake accounts */
     184           0 :   fd_accumulate_stake_infos(
     185           0 :       fd_bank_epoch_get( slot_ctx->bank ),
     186           0 :       stake_delegations,
     187           0 :       history,
     188           0 :       new_rate_activation_epoch,
     189           0 :       &accumulator );
     190             : 
     191             :   /* https://github.com/anza-xyz/agave/blob/v2.1.6/runtime/src/stakes.rs#L359 */
     192           0 :   fd_epoch_stake_history_entry_pair_t new_elem = {
     193           0 :     .epoch        = fd_bank_epoch_get( slot_ctx->bank ),
     194           0 :     .entry        = {
     195           0 :       .effective    = accumulator.effective,
     196           0 :       .activating   = accumulator.activating,
     197           0 :       .deactivating = accumulator.deactivating
     198           0 :     }
     199           0 :   };
     200             : 
     201           0 :   fd_sysvar_stake_history_update( slot_ctx, &new_elem, runtime_spad );
     202             : 
     203           0 : }
     204             : 
     205             : int
     206             : write_stake_state( fd_txn_account_t *    stake_acc_rec,
     207           0 :                    fd_stake_state_v2_t * stake_state ) {
     208             : 
     209           0 :   ulong encoded_stake_state_size = fd_stake_state_v2_size(stake_state);
     210             : 
     211           0 :   fd_bincode_encode_ctx_t ctx = {
     212           0 :     .data    = fd_txn_account_get_data_mut( stake_acc_rec ),
     213           0 :     .dataend = fd_txn_account_get_data_mut( stake_acc_rec ) + encoded_stake_state_size,
     214           0 :   };
     215           0 :   if( FD_UNLIKELY( fd_stake_state_v2_encode( stake_state, &ctx ) != FD_BINCODE_SUCCESS ) ) {
     216           0 :     FD_LOG_ERR(( "fd_stake_state_encode failed" ));
     217           0 :   }
     218             : 
     219           0 :   return 0;
     220           0 : }
     221             : 
     222             : /* Removes stake delegation from stakes */
     223             : static void
     224             : fd_stakes_remove_stake_delegation( fd_txn_account_t *   stake_account,
     225           0 :                                    fd_bank_t *          bank ) {
     226             : 
     227           0 :   fd_stake_delegations_t * stake_delegations_delta = fd_bank_stake_delegations_delta_locking_modify( bank );
     228           0 :   if( FD_UNLIKELY( !stake_delegations_delta ) ) {
     229           0 :     FD_LOG_CRIT(( "unable to retrieve join to stake delegation delta" ));
     230           0 :   }
     231             : 
     232           0 :   fd_stake_delegations_remove( stake_delegations_delta, stake_account->pubkey );
     233             : 
     234           0 :   fd_bank_stake_delegations_delta_end_locking_modify( bank );
     235           0 : }
     236             : 
     237             : /* Updates stake delegation in epoch stakes */
     238             : static void
     239             : fd_stakes_upsert_stake_delegation( fd_txn_account_t * stake_account,
     240           0 :                                    fd_bank_t *        bank ) {
     241             : 
     242           0 :   fd_stake_delegations_t * stake_delegations_delta = fd_bank_stake_delegations_delta_locking_modify( bank );
     243           0 :   if( FD_UNLIKELY( !stake_delegations_delta ) ) {
     244           0 :     FD_LOG_CRIT(( "unable to retrieve join to stake delegation delta" ));
     245           0 :   }
     246             : 
     247           0 :   fd_stake_state_v2_t stake_state;
     248           0 :   int err = fd_stake_get_state( stake_account, &stake_state );
     249           0 :   if( FD_UNLIKELY( err != 0 ) ) {
     250           0 :     FD_LOG_WARNING(( "Failed to get stake state" ));
     251           0 :     fd_bank_stake_delegations_delta_end_locking_modify( bank );
     252           0 :     return;
     253           0 :   }
     254             : 
     255           0 :   if( FD_UNLIKELY( !fd_stake_state_v2_is_stake( &stake_state ) ) ) {
     256           0 :     FD_LOG_WARNING(( "Not a valid stake" ));
     257           0 :     fd_bank_stake_delegations_delta_end_locking_modify( bank );
     258           0 :     return;
     259           0 :   }
     260             : 
     261           0 :   if( FD_UNLIKELY( stake_state.inner.stake.stake.delegation.stake==0UL ) ) {
     262           0 :     FD_LOG_WARNING(( "Stake is empty" ));
     263           0 :     fd_bank_stake_delegations_delta_end_locking_modify( bank );
     264           0 :     return;
     265           0 :   }
     266             : 
     267           0 :   fd_stake_delegations_update(
     268           0 :       stake_delegations_delta,
     269           0 :       stake_account->pubkey,
     270           0 :       &stake_state.inner.stake.stake.delegation.voter_pubkey,
     271           0 :       stake_state.inner.stake.stake.delegation.stake,
     272           0 :       stake_state.inner.stake.stake.delegation.activation_epoch,
     273           0 :       stake_state.inner.stake.stake.delegation.deactivation_epoch,
     274           0 :       stake_state.inner.stake.stake.credits_observed,
     275           0 :       stake_state.inner.stake.stake.delegation.warmup_cooldown_rate );
     276             : 
     277           0 :   fd_bank_stake_delegations_delta_end_locking_modify( bank );
     278           0 : }
     279             : 
     280             : void
     281             : fd_update_stake_delegation( fd_txn_account_t * stake_account,
     282           0 :                             fd_bank_t *        bank ) {
     283             : 
     284           0 :   int is_empty  = fd_txn_account_get_lamports( stake_account )==0UL;
     285           0 :   int is_uninit = 1;
     286           0 :   if( fd_txn_account_get_data_len( stake_account )>=4UL ) {
     287           0 :     uint prefix = FD_LOAD( uint, fd_txn_account_get_data( stake_account ) );
     288           0 :     is_uninit = ( prefix==fd_stake_state_v2_enum_uninitialized );
     289           0 :   }
     290             : 
     291           0 :   if( is_empty || is_uninit ) {
     292           0 :     fd_stakes_remove_stake_delegation( stake_account, bank );
     293           0 :   } else {
     294           0 :     fd_stakes_upsert_stake_delegation( stake_account, bank );
     295           0 :   }
     296           0 : }

Generated by: LCOV version 1.14