LCOV - code coverage report
Current view: top level - flamenco/stakes - fd_stakes.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 177 0.0 %
Date: 2025-11-08 04:42:58 Functions: 0 6 0.0 %

          Line data    Source code
       1             : #include "fd_stakes.h"
       2             : #include "../runtime/fd_bank.h"
       3             : #include "../runtime/fd_system_ids.h"
       4             : #include "../runtime/program/fd_stake_program.h"
       5             : #include "../runtime/program/fd_vote_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             : /* We need to update the amount of stake that each vote account has for
      29             :    the given epoch.  This can only be done after the stake history
      30             :    sysvar has been updated.  We also cache the stakes for each of the
      31             :    vote accounts for the previous epoch.
      32             : 
      33             :    https://github.com/anza-xyz/agave/blob/v3.0.4/runtime/src/stakes.rs#L471 */
      34             : void
      35             : fd_refresh_vote_accounts( fd_bank_t *                    bank,
      36             :                           fd_stake_delegations_t const * stake_delegations,
      37             :                           fd_stake_history_t const *     history,
      38           0 :                           ulong *                        new_rate_activation_epoch ) {
      39             : 
      40           0 :   ulong epoch = fd_bank_epoch_get( bank );
      41             : 
      42           0 :   ulong total_stake = 0UL;
      43             : 
      44           0 :   fd_vote_states_t * vote_states = fd_bank_vote_states_locking_modify( bank );
      45           0 :   if( FD_UNLIKELY( !vote_states ) ) {
      46           0 :     FD_LOG_CRIT(( "vote_states is NULL" ));
      47           0 :   }
      48             : 
      49             :   /* Reset the vote stakes so we can re-compute them based on the most
      50             :      current stake delegation values. */
      51           0 :   fd_vote_states_reset_stakes( vote_states );
      52             : 
      53           0 :   fd_stake_delegations_iter_t iter_[1];
      54           0 :   for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations );
      55           0 :        !fd_stake_delegations_iter_done( iter );
      56           0 :        fd_stake_delegations_iter_next( iter ) ) {
      57           0 :     fd_stake_delegation_t const * stake_delegation = fd_stake_delegations_iter_ele( iter );
      58             : 
      59           0 :     fd_delegation_t delegation = {
      60           0 :       .voter_pubkey         = stake_delegation->vote_account,
      61           0 :       .stake                = stake_delegation->stake,
      62           0 :       .deactivation_epoch   = stake_delegation->deactivation_epoch,
      63           0 :       .activation_epoch     = stake_delegation->activation_epoch,
      64           0 :       .warmup_cooldown_rate = stake_delegation->warmup_cooldown_rate,
      65           0 :     };
      66             : 
      67           0 :     fd_stake_history_entry_t new_entry = fd_stake_activating_and_deactivating(
      68           0 :         &delegation,
      69           0 :         epoch,
      70           0 :         history,
      71           0 :         new_rate_activation_epoch );
      72             : 
      73           0 :     fd_vote_state_ele_t * vote_state = fd_vote_states_query( vote_states, &stake_delegation->vote_account );
      74           0 :     if( FD_LIKELY( vote_state ) ) {
      75           0 :       total_stake       += new_entry.effective;
      76           0 :       vote_state->stake += new_entry.effective;
      77           0 :     }
      78           0 :   }
      79             : 
      80           0 :   fd_bank_total_epoch_stake_set( bank, total_stake );
      81             : 
      82             :   /* This corresponding logic does not exist in the Agave client.  The
      83             :      stakes from epoch T-2 are cached in the vote states struct in order
      84             :      to make clock calulations more efficient.  This is purely an
      85             :      optimization. */
      86             : 
      87           0 :   fd_vote_states_t const * vote_states_prev = fd_bank_vote_states_prev_prev_locking_query( bank );
      88             : 
      89           0 :   if( FD_LIKELY( fd_bank_slot_get( bank )!=0UL ) ) {
      90           0 :     fd_vote_states_iter_t vs_iter_[1];
      91           0 :     for( fd_vote_states_iter_t * vs_iter = fd_vote_states_iter_init( vs_iter_, vote_states );
      92           0 :         !fd_vote_states_iter_done( vs_iter );
      93           0 :         fd_vote_states_iter_next( vs_iter ) ) {
      94           0 :       fd_vote_state_ele_t * vote_state      = fd_vote_states_iter_ele( vs_iter );
      95           0 :       fd_vote_state_ele_t * vote_state_prev = fd_vote_states_query( vote_states_prev, &vote_state->vote_account );
      96           0 :       vote_state->stake_t_2 = !!vote_state_prev ? vote_state_prev->stake : 0UL;
      97           0 :     }
      98           0 :   }
      99           0 :   fd_bank_vote_states_prev_prev_end_locking_query( bank );
     100           0 :   fd_bank_vote_states_end_locking_modify( bank );
     101           0 : }
     102             : 
     103             : /* https://github.com/anza-xyz/agave/blob/v3.0.4/runtime/src/stakes.rs#L280 */
     104             : void
     105             : fd_stakes_activate_epoch( fd_bank_t *                    bank,
     106             :                           fd_accdb_user_t *              accdb,
     107             :                           fd_funk_txn_xid_t const *      xid,
     108             :                           fd_capture_ctx_t *             capture_ctx,
     109             :                           fd_stake_delegations_t const * stake_delegations,
     110           0 :                           ulong *                        new_rate_activation_epoch ) {
     111             : 
     112             :   /* First, we need to accumulate the stats for the current amount of
     113             :      effective, activating, and deactivating stake for the current
     114             :      epoch.  Once this is computed, we can add update our stake history
     115             :      sysvar.  Afterward, we can refresh the stake values for the vote
     116             :      accounts for the new epoch. */
     117             : 
     118           0 :   fd_stake_history_t stake_history[1];
     119           0 :   if( FD_UNLIKELY( !fd_sysvar_stake_history_read( accdb->funk, xid, stake_history ) ) ) {
     120           0 :     FD_LOG_ERR(( "StakeHistory sysvar is missing from sysvar cache" ));
     121           0 :   }
     122             : 
     123           0 :   fd_epoch_stake_history_entry_pair_t new_elem = {
     124           0 :     .epoch = fd_bank_epoch_get( bank ),
     125           0 :     .entry = {
     126           0 :       .effective    = 0UL,
     127           0 :       .activating   = 0UL,
     128           0 :       .deactivating = 0UL
     129           0 :     }
     130           0 :   };
     131             : 
     132           0 :   fd_stake_delegations_iter_t iter_[1];
     133           0 :   for( fd_stake_delegations_iter_t * iter = fd_stake_delegations_iter_init( iter_, stake_delegations );
     134           0 :        !fd_stake_delegations_iter_done( iter );
     135           0 :        fd_stake_delegations_iter_next( iter ) ) {
     136           0 :     fd_stake_delegation_t const * stake_delegation = fd_stake_delegations_iter_ele( iter );
     137             : 
     138           0 :     fd_delegation_t delegation = {
     139           0 :       .voter_pubkey         = stake_delegation->vote_account,
     140           0 :       .stake                = stake_delegation->stake,
     141           0 :       .activation_epoch     = stake_delegation->activation_epoch,
     142           0 :       .deactivation_epoch   = stake_delegation->deactivation_epoch,
     143           0 :       .warmup_cooldown_rate = stake_delegation->warmup_cooldown_rate,
     144           0 :     };
     145             : 
     146           0 :     fd_stake_history_entry_t new_entry = fd_stake_activating_and_deactivating(
     147           0 :         &delegation,
     148           0 :         fd_bank_epoch_get( bank ),
     149           0 :         stake_history,
     150           0 :         new_rate_activation_epoch );
     151           0 :     new_elem.entry.effective    += new_entry.effective;
     152           0 :     new_elem.entry.activating   += new_entry.activating;
     153           0 :     new_elem.entry.deactivating += new_entry.deactivating;
     154           0 :   }
     155             : 
     156           0 :   fd_sysvar_stake_history_update( bank, accdb, xid, capture_ctx, &new_elem );
     157             : 
     158           0 :   if( FD_UNLIKELY( !fd_sysvar_stake_history_read( accdb->funk, xid, stake_history ) ) ) {
     159           0 :     FD_LOG_ERR(( "StakeHistory sysvar is missing from sysvar cache" ));
     160           0 :   }
     161             : 
     162             :   /* Now increment the epoch and recompute the stakes for the vote
     163             :      accounts for the new epoch value. */
     164             : 
     165           0 :   fd_bank_epoch_set( bank, fd_bank_epoch_get( bank ) + 1UL );
     166             : 
     167           0 :   fd_refresh_vote_accounts( bank,
     168           0 :                             stake_delegations,
     169           0 :                             stake_history,
     170           0 :                             new_rate_activation_epoch );
     171             : 
     172           0 : }
     173             : 
     174             : int
     175             : write_stake_state( fd_txn_account_t *    stake_acc_rec,
     176           0 :                    fd_stake_state_v2_t * stake_state ) {
     177             : 
     178           0 :   ulong encoded_stake_state_size = fd_stake_state_v2_size(stake_state);
     179             : 
     180           0 :   fd_bincode_encode_ctx_t ctx = {
     181           0 :     .data    = fd_txn_account_get_data_mut( stake_acc_rec ),
     182           0 :     .dataend = fd_txn_account_get_data_mut( stake_acc_rec ) + encoded_stake_state_size,
     183           0 :   };
     184           0 :   if( FD_UNLIKELY( fd_stake_state_v2_encode( stake_state, &ctx ) != FD_BINCODE_SUCCESS ) ) {
     185           0 :     FD_LOG_ERR(( "fd_stake_state_encode failed" ));
     186           0 :   }
     187             : 
     188           0 :   return 0;
     189           0 : }
     190             : 
     191             : void
     192             : fd_stakes_update_stake_delegation( fd_txn_account_t * stake_account,
     193           0 :                                    fd_bank_t *        bank ) {
     194             : 
     195           0 :   if( !stake_account->is_mutable ) return;
     196             : 
     197           0 :   fd_stake_delegations_t * stake_delegations_delta = fd_bank_stake_delegations_delta_locking_modify( bank );
     198           0 :   if( FD_UNLIKELY( !stake_delegations_delta ) ) {
     199           0 :     FD_LOG_CRIT(( "unable to retrieve join to stake delegation delta" ));
     200           0 :   }
     201             : 
     202           0 :   if( fd_txn_account_get_lamports( stake_account )==0UL ) {
     203           0 :     fd_stake_delegations_remove( stake_delegations_delta, stake_account->pubkey );
     204           0 :     fd_bank_stake_delegations_delta_end_locking_modify( bank );
     205           0 :     return;
     206           0 :   }
     207             : 
     208           0 :   fd_stake_state_v2_t stake_state;
     209           0 :   int err = fd_stake_get_state( stake_account, &stake_state );
     210           0 :   if( FD_UNLIKELY( err!=0 ) ) {
     211           0 :     fd_stake_delegations_remove( stake_delegations_delta, stake_account->pubkey );
     212           0 :     fd_bank_stake_delegations_delta_end_locking_modify( bank );
     213           0 :     return;
     214           0 :   }
     215             : 
     216           0 :   if( FD_UNLIKELY( !fd_stake_state_v2_is_stake( &stake_state ) ) ) {
     217           0 :     fd_stake_delegations_remove( stake_delegations_delta, stake_account->pubkey );
     218           0 :     fd_bank_stake_delegations_delta_end_locking_modify( bank );
     219           0 :     return;
     220           0 :   }
     221             : 
     222           0 :   if( FD_UNLIKELY( fd_stake_state_v2_is_uninitialized( &stake_state ) ) ) {
     223           0 :     fd_stake_delegations_remove( stake_delegations_delta, stake_account->pubkey );
     224           0 :     fd_bank_stake_delegations_delta_end_locking_modify( bank );
     225           0 :     return;
     226           0 :   }
     227             : 
     228           0 :   if( FD_UNLIKELY( stake_state.inner.stake.stake.delegation.stake==0UL ) ) {
     229           0 :     fd_stake_delegations_remove( stake_delegations_delta, stake_account->pubkey );
     230           0 :     fd_bank_stake_delegations_delta_end_locking_modify( bank );
     231           0 :     return;
     232           0 :   }
     233             : 
     234           0 :   fd_stake_delegations_update( stake_delegations_delta,
     235           0 :                                stake_account->pubkey,
     236           0 :                                &stake_state.inner.stake.stake.delegation.voter_pubkey,
     237           0 :                                stake_state.inner.stake.stake.delegation.stake,
     238           0 :                                stake_state.inner.stake.stake.delegation.activation_epoch,
     239           0 :                                stake_state.inner.stake.stake.delegation.deactivation_epoch,
     240           0 :                                stake_state.inner.stake.stake.credits_observed,
     241           0 :                                stake_state.inner.stake.stake.delegation.warmup_cooldown_rate );
     242             : 
     243           0 :   fd_bank_stake_delegations_delta_end_locking_modify( bank );
     244           0 : }
     245             : 
     246             : void
     247             : fd_stakes_update_vote_state( fd_txn_account_t * vote_account,
     248           0 :                              fd_bank_t *        bank ) {
     249             : 
     250           0 :   if( !vote_account->is_mutable ) return;
     251             : 
     252           0 :   fd_vote_states_t * vote_states = fd_bank_vote_states_locking_modify( bank );
     253             : 
     254           0 :   if( fd_txn_account_get_lamports( vote_account )==0UL ) {
     255           0 :     fd_vote_states_remove( vote_states, vote_account->pubkey );
     256           0 :     fd_bank_vote_states_end_locking_modify( bank );
     257           0 :     return;
     258           0 :   }
     259             : 
     260           0 :   if( !fd_vote_state_versions_is_correct_and_initialized( vote_account ) ) {
     261           0 :     fd_vote_states_remove( vote_states, vote_account->pubkey );
     262           0 :     fd_bank_vote_states_end_locking_modify( bank );
     263           0 :     return;
     264           0 :   }
     265             : 
     266           0 :   fd_vote_states_update_from_account( vote_states,
     267           0 :                                       vote_account->pubkey,
     268           0 :                                       fd_txn_account_get_data( vote_account ),
     269           0 :                                       fd_txn_account_get_data_len( vote_account ) );
     270           0 :   fd_bank_vote_states_end_locking_modify( bank );
     271           0 : }

Generated by: LCOV version 1.14