LCOV - code coverage report
Current view: top level - flamenco/runtime/program/vote - fd_vote_state_versioned.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 35 511 6.8 %
Date: 2026-06-29 05:51:35 Functions: 5 36 13.9 %

          Line data    Source code
       1             : #include "fd_vote_state_versioned.h"
       2             : #include "fd_vote_utils.h"
       3             : #include "fd_vote_state_v3.h"
       4             : #include "fd_vote_state_v4.h"
       5             : #include "fd_authorized_voters.h"
       6             : #include "../fd_vote_program.h"
       7             : #include "../../fd_runtime.h"
       8             : #include "../../fd_system_ids.h"
       9             : 
      10             : /* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L42 */
      11             : #define DEFAULT_PRIOR_VOTERS_OFFSET 114
      12             : 
      13             : /* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L886 */
      14             : #define VERSION_OFFSET (4UL)
      15             : 
      16             : /* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L887 */
      17             : #define DEFAULT_PRIOR_VOTERS_END (118)
      18             : 
      19             : /* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L6 */
      20             : #define DEFAULT_PRIOR_VOTERS_OFFSET_1_14_11 (82UL)
      21             : 
      22             : /* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L60 */
      23             : #define DEFAULT_PRIOR_VOTERS_END_1_14_11 (86UL)
      24             : 
      25             : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L780-L785 */
      26             : static inline fd_vote_lockout_t *
      27           0 : last_lockout( fd_vote_state_versioned_t * self ) {
      28           0 :   fd_landed_vote_t * votes = NULL;
      29           0 :   switch( self->kind ) {
      30           0 :     case fd_vote_state_versioned_enum_v3:
      31           0 :       votes = self->v3.votes;
      32           0 :       break;
      33           0 :     case fd_vote_state_versioned_enum_v4:
      34           0 :       votes = self->v4.votes;
      35           0 :       break;
      36           0 :     default:
      37           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
      38           0 :   }
      39             : 
      40           0 :   if( deq_fd_landed_vote_t_empty( votes ) ) return NULL;
      41           0 :   fd_landed_vote_t * last_vote = deq_fd_landed_vote_t_peek_tail( votes );
      42           0 :   return &last_vote->lockout;
      43           0 : }
      44             : 
      45             : /**********************************************************************/
      46             : /* Getters                                                            */
      47             : /**********************************************************************/
      48             : 
      49             : int
      50             : fd_vsv_get_state( fd_acc_t const *            acc,
      51           6 :                   fd_vote_state_versioned_t * versioned ) {
      52           6 :   if( FD_UNLIKELY( !fd_vote_state_versioned_deserialize( versioned, acc->data, acc->data_len ) ) ) {
      53           0 :     return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
      54           0 :   }
      55             : 
      56           6 :   return FD_EXECUTOR_INSTR_SUCCESS;
      57           6 : }
      58             : 
      59             : int
      60             : fd_vsv_deserialize( fd_acc_t const *            acc,
      61           0 :                     fd_vote_state_versioned_t * versioned ) {
      62           0 :   int rc = fd_vsv_get_state( acc, versioned );
      63           0 :   if( FD_UNLIKELY( rc ) ) return rc;
      64             : 
      65           0 :   if( FD_UNLIKELY( versioned->kind==fd_vote_state_versioned_enum_uninitialized ) ) {
      66           0 :     return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
      67           0 :   }
      68             : 
      69           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
      70           0 : }
      71             : 
      72             : fd_pubkey_t const *
      73           0 : fd_vsv_get_authorized_withdrawer( fd_vote_state_versioned_t * self ) {
      74           0 :   switch( self->kind ) {
      75           0 :     case fd_vote_state_versioned_enum_v1_14_11:
      76           0 :       return &self->v1_14_11.authorized_withdrawer;
      77           0 :     case fd_vote_state_versioned_enum_v3:
      78           0 :       return &self->v3.authorized_withdrawer;
      79           0 :     case fd_vote_state_versioned_enum_v4:
      80           0 :       return &self->v4.authorized_withdrawer;
      81           0 :     default:
      82           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
      83           0 :   }
      84           0 : }
      85             : 
      86             : uchar
      87           0 : fd_vsv_get_commission( fd_vote_state_versioned_t * self ) {
      88           0 :   switch( self->kind ) {
      89           0 :     case fd_vote_state_versioned_enum_v3:
      90           0 :       return self->v3.commission;
      91           0 :     case fd_vote_state_versioned_enum_v4:
      92           0 :       return (uchar)fd_ushort_min( (ushort)UCHAR_MAX, (ushort)( self->v4.inflation_rewards_commission_bps/100 ) );
      93           0 :     default:
      94           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
      95           0 :   }
      96           0 : }
      97             : 
      98             : fd_vote_epoch_credits_t const *
      99           0 : fd_vsv_get_epoch_credits( fd_vote_state_versioned_t * self ) {
     100           0 :   return fd_vsv_get_epoch_credits_mutable( self );
     101           0 : }
     102             : 
     103             : fd_landed_vote_t const *
     104           0 : fd_vsv_get_votes( fd_vote_state_versioned_t * self ) {
     105           0 :   return fd_vsv_get_votes_mutable( self );
     106           0 : }
     107             : 
     108             : ulong const *
     109           0 : fd_vsv_get_last_voted_slot( fd_vote_state_versioned_t * self ) {
     110           0 :   fd_vote_lockout_t * last_lockout_ = last_lockout( self );
     111           0 :   if( FD_UNLIKELY( !last_lockout_ ) ) return NULL;
     112           0 :   return &last_lockout_->slot;
     113           0 : }
     114             : 
     115             : ulong const *
     116           0 : fd_vsv_get_root_slot( fd_vote_state_versioned_t * self ) {
     117           0 :   switch( self->kind ) {
     118           0 :     case fd_vote_state_versioned_enum_v3:
     119           0 :       if( !self->v3.has_root_slot ) return NULL;
     120           0 :       return &self->v3.root_slot;
     121           0 :     case fd_vote_state_versioned_enum_v4:
     122           0 :       if( !self->v4.has_root_slot ) return NULL;
     123           0 :       return &self->v4.root_slot;
     124           0 :     default:
     125           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     126           0 :   }
     127           0 : }
     128             : 
     129             : fd_vote_block_timestamp_t const *
     130           0 : fd_vsv_get_last_timestamp( fd_vote_state_versioned_t * self ) {
     131           0 :   switch( self->kind ) {
     132           0 :     case fd_vote_state_versioned_enum_v3:
     133           0 :       return &self->v3.last_timestamp;
     134           0 :     case fd_vote_state_versioned_enum_v4:
     135           0 :       return &self->v4.last_timestamp;
     136           0 :     default:
     137           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     138           0 :   }
     139           0 : }
     140             : 
     141             : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/handler.rs#L938 */
     142             : int
     143           0 : fd_vsv_has_bls_pubkey( fd_vote_state_versioned_t * self ) {
     144           0 :   switch( self->kind ) {
     145           0 :     case fd_vote_state_versioned_enum_uninitialized:
     146           0 :       return 0;
     147           0 :     case fd_vote_state_versioned_enum_v1_14_11:
     148           0 :       return 0;
     149           0 :     case fd_vote_state_versioned_enum_v3:
     150             :       /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/handler.rs#L483 */
     151           0 :       return 0;
     152           0 :     case fd_vote_state_versioned_enum_v4:
     153             :       /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/handler.rs#L676 */
     154           0 :       return !!self->v4.has_bls_pubkey_compressed;
     155           0 :     default:
     156           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     157           0 :   }
     158           0 : }
     159             : 
     160             : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/handler.rs#L823-L828 */
     161             : ulong
     162           0 : fd_vsv_get_pending_delegator_rewards( fd_vote_state_versioned_t * self ) {
     163           0 :   switch( self->kind ) {
     164           0 :     case fd_vote_state_versioned_enum_v3:
     165           0 :       return 0UL;
     166           0 :     case fd_vote_state_versioned_enum_v4:
     167           0 :       return self->v4.pending_delegator_rewards;
     168           0 :     default:
     169           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     170           0 :   }
     171           0 : }
     172             : 
     173             : /**********************************************************************/
     174             : /* Mutable getters                                                    */
     175             : /**********************************************************************/
     176             : 
     177             : fd_vote_epoch_credits_t *
     178           0 : fd_vsv_get_epoch_credits_mutable( fd_vote_state_versioned_t * self ) {
     179           0 :   switch( self->kind ) {
     180           0 :     case fd_vote_state_versioned_enum_v3:
     181           0 :       return self->v3.epoch_credits;
     182           0 :     case fd_vote_state_versioned_enum_v4:
     183           0 :       return self->v4.epoch_credits;
     184           0 :     default:
     185           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     186           0 :   }
     187           0 : }
     188             : 
     189             : fd_landed_vote_t *
     190           0 : fd_vsv_get_votes_mutable( fd_vote_state_versioned_t * self ) {
     191           0 :   switch( self->kind ) {
     192           0 :     case fd_vote_state_versioned_enum_v3:
     193           0 :       return self->v3.votes;
     194           0 :     case fd_vote_state_versioned_enum_v4:
     195           0 :       return self->v4.votes;
     196           0 :     default:
     197           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     198           0 :   }
     199           0 : }
     200             : 
     201             : /**********************************************************************/
     202             : /* Setters                                                            */
     203             : /**********************************************************************/
     204             : 
     205             : int
     206             : fd_vsv_set_state( fd_borrowed_account_t *     self,
     207           6 :                   fd_vote_state_versioned_t * state ) {
     208             :   /* https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L974 */
     209           6 :   uchar * data = NULL;
     210           6 :   ulong   dlen = 0UL;
     211           6 :   int err = fd_borrowed_account_get_data_mut( self, &data, &dlen );
     212           6 :   if( FD_UNLIKELY( err ) ) {
     213           0 :     return err;
     214           0 :   }
     215             : 
     216             :   /* Note that although the serialization method already performs bounds
     217             :      checks, the account data buffer should remain unmodified if the
     218             :      serialization would fail.
     219             :      https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/src/transaction_context.rs#L978 */
     220           6 :   ulong serialized_size = fd_vote_state_versioned_serialized_size( state );
     221           6 :   if( FD_UNLIKELY( serialized_size>dlen ) ) {
     222           0 :     return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
     223           0 :   }
     224             : 
     225             :   /* https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/src/transaction_context.rs#L983 */
     226           6 :   if( FD_UNLIKELY( fd_vote_state_versioned_serialize( state, data, dlen ) ) ) {
     227           0 :     FD_LOG_CRIT(( "invariant violation: fd_vote_state_versioned_serialize failed" ));
     228           0 :   }
     229             : 
     230           6 :   return FD_EXECUTOR_INSTR_SUCCESS;
     231           6 : }
     232             : 
     233             : int
     234             : fd_vsv_set_vote_account_state( fd_exec_instr_ctx_t const * ctx,
     235             :                                fd_borrowed_account_t *     vote_account,
     236           0 :                                fd_vote_state_versioned_t * versioned ) {
     237           0 :   switch( versioned->kind ) {
     238           0 :     case fd_vote_state_versioned_enum_v3:
     239           0 :       return fd_vote_state_v3_set_vote_account_state( ctx, vote_account, versioned );
     240           0 :     case fd_vote_state_versioned_enum_v4:
     241           0 :       return fd_vote_state_v4_set_vote_account_state( ctx, vote_account, versioned );
     242           0 :     default:
     243           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", versioned->kind ));
     244           0 :   }
     245           0 : }
     246             : 
     247             : void
     248             : fd_vsv_set_authorized_withdrawer( fd_vote_state_versioned_t * self,
     249           0 :                                   fd_pubkey_t const *         authorized_withdrawer ) {
     250           0 :   switch( self->kind ) {
     251           0 :     case fd_vote_state_versioned_enum_v3: {
     252           0 :       self->v3.authorized_withdrawer = *authorized_withdrawer;
     253           0 :       break;
     254           0 :     }
     255           0 :     case fd_vote_state_versioned_enum_v4: {
     256           0 :       self->v4.authorized_withdrawer = *authorized_withdrawer;
     257           0 :       break;
     258           0 :     }
     259           0 :     default:
     260           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     261           0 :   }
     262           0 : }
     263             : 
     264             : int
     265             : fd_vsv_set_new_authorized_voter( fd_exec_instr_ctx_t *       ctx,
     266             :                                  fd_vote_state_versioned_t * self,
     267             :                                  fd_pubkey_t const *         authorized_pubkey,
     268             :                                  ulong                       current_epoch,
     269             :                                  ulong                       target_epoch,
     270             :                                  uchar const *               bls_pubkey,
     271             :                                  int                         authorized_withdrawer_signer,
     272             :                                  fd_pubkey_t const *         signers[ FD_TXN_SIG_MAX ],
     273           0 :                                  ulong                       signers_cnt ) {
     274           0 :   switch( self->kind ) {
     275           0 :     case fd_vote_state_versioned_enum_v3:
     276           0 :       return fd_vote_state_v3_set_new_authorized_voter(
     277           0 :           ctx,
     278           0 :           &self->v3,
     279           0 :           authorized_pubkey,
     280           0 :           current_epoch,
     281           0 :           target_epoch,
     282           0 :           bls_pubkey,
     283           0 :           authorized_withdrawer_signer,
     284           0 :           signers,
     285           0 :           signers_cnt
     286           0 :       );
     287           0 :     case fd_vote_state_versioned_enum_v4:
     288           0 :       return fd_vote_state_v4_set_new_authorized_voter(
     289           0 :           ctx,
     290           0 :           &self->v4,
     291           0 :           authorized_pubkey,
     292           0 :           current_epoch,
     293           0 :           target_epoch,
     294           0 :           bls_pubkey,
     295           0 :           authorized_withdrawer_signer,
     296           0 :           signers,
     297           0 :           signers_cnt
     298           0 :       );
     299           0 :     default:
     300           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     301           0 :   }
     302           0 : }
     303             : 
     304             : void
     305             : fd_vsv_set_node_pubkey( fd_vote_state_versioned_t * self,
     306           0 :                         fd_pubkey_t const *         node_pubkey ) {
     307           0 :   switch( self->kind ) {
     308           0 :     case fd_vote_state_versioned_enum_v3:
     309           0 :       self->v3.node_pubkey = *node_pubkey;
     310           0 :       break;
     311           0 :     case fd_vote_state_versioned_enum_v4:
     312           0 :       self->v4.node_pubkey = *node_pubkey;
     313           0 :       break;
     314           0 :     default:
     315           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     316           0 :   }
     317           0 : }
     318             : 
     319             : void
     320             : fd_vsv_set_block_revenue_collector( fd_vote_state_versioned_t * self,
     321           0 :                                     fd_pubkey_t const *         block_revenue_collector ) {
     322           0 :   switch( self->kind ) {
     323           0 :     case fd_vote_state_versioned_enum_v4:
     324           0 :       self->v4.block_revenue_collector = *block_revenue_collector;
     325           0 :       break;
     326           0 :     case fd_vote_state_versioned_enum_v3:
     327             :       /* No-op for v3 */
     328           0 :       break;
     329           0 :     default:
     330           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     331           0 :   }
     332           0 : }
     333             : 
     334             : void
     335             : fd_vsv_set_commission( fd_vote_state_versioned_t * self,
     336           0 :                        uchar                       commission ) {
     337           0 :   switch( self->kind ) {
     338           0 :     case fd_vote_state_versioned_enum_v3:
     339           0 :       self->v3.commission = commission;
     340           0 :       break;
     341           0 :     case fd_vote_state_versioned_enum_v4:
     342           0 :       self->v4.inflation_rewards_commission_bps = (ushort)( commission*100 );
     343           0 :       break;
     344           0 :     default:
     345           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     346           0 :   }
     347           0 : }
     348             : 
     349             : void
     350             : fd_vsv_set_inflation_rewards_commission_bps( fd_vote_state_versioned_t * self,
     351           0 :                                              ushort                      commission_bps ) {
     352           0 :   switch( self->kind ) {
     353           0 :     case fd_vote_state_versioned_enum_v4:
     354           0 :       self->v4.inflation_rewards_commission_bps = commission_bps;
     355           0 :       break;
     356           0 :     case fd_vote_state_versioned_enum_v3:
     357             :       /* No-op for v3 */
     358           0 :       break;
     359           0 :     default:
     360           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     361           0 :   }
     362           0 : }
     363             : 
     364             : void
     365             : fd_vsv_set_block_revenue_commission_bps( fd_vote_state_versioned_t * self,
     366           0 :                                          ushort                      commission_bps ) {
     367           0 :   switch( self->kind ) {
     368           0 :     case fd_vote_state_versioned_enum_v4:
     369           0 :       self->v4.block_revenue_commission_bps = commission_bps;
     370           0 :       break;
     371           0 :     case fd_vote_state_versioned_enum_v3:
     372             :       /* No-op for v3 */
     373           0 :       break;
     374           0 :     default:
     375           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     376           0 :   }
     377           0 : }
     378             : 
     379             : void
     380           0 : fd_vsv_set_root_slot( fd_vote_state_versioned_t * self, ulong * root_slot ) {
     381           0 :   switch( self->kind ) {
     382           0 :     case fd_vote_state_versioned_enum_v3:
     383           0 :       self->v3.has_root_slot = (root_slot!=NULL);
     384           0 :       if( FD_LIKELY( root_slot ) ) {
     385           0 :         self->v3.root_slot = *root_slot;
     386           0 :       }
     387           0 :       break;
     388           0 :     case fd_vote_state_versioned_enum_v4:
     389           0 :       self->v4.has_root_slot = (root_slot!=NULL);
     390           0 :       if( FD_LIKELY( root_slot ) ) {
     391           0 :         self->v4.root_slot = *root_slot;
     392           0 :       }
     393           0 :       break;
     394           0 :     default:
     395           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     396           0 :   }
     397           0 : }
     398             : 
     399             : static void
     400             : fd_vsv_set_last_timestamp( fd_vote_state_versioned_t *       self,
     401           0 :                            fd_vote_block_timestamp_t const * last_timestamp ) {
     402           0 :   switch( self->kind ) {
     403           0 :     case fd_vote_state_versioned_enum_v3:
     404           0 :       self->v3.last_timestamp = *last_timestamp;
     405           0 :       break;
     406           0 :     case fd_vote_state_versioned_enum_v4:
     407           0 :       self->v4.last_timestamp = *last_timestamp;
     408           0 :       break;
     409           0 :     default:
     410           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     411           0 :   }
     412           0 : }
     413             : 
     414             : /**********************************************************************/
     415             : /* General functions                                                  */
     416             : /**********************************************************************/
     417             : 
     418             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L855
     419             : static void
     420           0 : double_lockouts( fd_vote_state_versioned_t * self ) {
     421           0 :   fd_landed_vote_t * votes = fd_vsv_get_votes_mutable( self );
     422             : 
     423             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L856
     424           0 :   ulong stack_depth = deq_fd_landed_vote_t_cnt( votes );
     425           0 :   ulong i           = 0;
     426             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L857
     427           0 :   for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( votes );
     428           0 :        !deq_fd_landed_vote_t_iter_done( votes, iter );
     429           0 :        iter = deq_fd_landed_vote_t_iter_next( votes, iter ) ) {
     430           0 :     fd_landed_vote_t * v = deq_fd_landed_vote_t_iter_ele( votes, iter );
     431             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L860
     432           0 :     if( stack_depth >
     433           0 :         fd_ulong_checked_add_expect(
     434           0 :             i,
     435           0 :             (ulong)v->lockout.confirmation_count,
     436           0 :             "`confirmation_count` and tower_size should be bounded by `MAX_LOCKOUT_HISTORY`" ) )
     437           0 :       {
     438             :         // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L864
     439           0 :         fd_vote_lockout_increase_confirmation_count( &v->lockout, 1 );
     440           0 :       }
     441           0 :     i++;
     442           0 :   }
     443           0 : }
     444             : 
     445             : void
     446             : fd_vsv_increment_credits( fd_vote_state_versioned_t * self,
     447             :                           ulong                       epoch,
     448           0 :                           ulong                       credits ) {
     449           0 :   fd_vote_epoch_credits_t * epoch_credits = fd_vsv_get_epoch_credits_mutable( self );
     450             : 
     451             :   /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L286-L305 */
     452           0 :   if( FD_UNLIKELY( deq_fd_vote_epoch_credits_t_empty( epoch_credits ) ) ) {
     453             :     /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L286-L288 */
     454           0 :     deq_fd_vote_epoch_credits_t_push_tail_wrap(
     455           0 :         epoch_credits,
     456           0 :         ( fd_vote_epoch_credits_t ){ .epoch = epoch, .credits = 0, .prev_credits = 0 } );
     457           0 :   } else if( FD_LIKELY( epoch !=
     458           0 :                         deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits )->epoch ) ) {
     459             :     /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L290 */
     460           0 :     fd_vote_epoch_credits_t * last = deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits );
     461             : 
     462           0 :     ulong credits      = last->credits;
     463           0 :     ulong prev_credits = last->prev_credits;
     464             : 
     465             :     /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L292-L299 */
     466           0 :     if( FD_LIKELY( credits!=prev_credits ) ) {
     467           0 :       if( FD_UNLIKELY( deq_fd_vote_epoch_credits_t_cnt( epoch_credits )>=MAX_EPOCH_CREDITS_HISTORY ) ) {
     468             :         /* Although Agave performs a `.remove(0)` AFTER the call to
     469             :           `.push()`, there is an edge case where the epoch credits is
     470             :           full, making the call to `_push_tail()` unsafe. Since Agave's
     471             :           structures are dynamically allocated, it is safe for them to
     472             :           simply call `.push()` and then popping afterwards. We have to
     473             :           reverse the order of operations to maintain correct behavior
     474             :           and avoid overflowing the deque.
     475             :           https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L303 */
     476           0 :         deq_fd_vote_epoch_credits_t_pop_head( epoch_credits );
     477           0 :       }
     478             : 
     479             :       /* This will not fail because we already popped if we're at
     480             :          capacity, since the epoch_credits deque is allocated with a
     481             :          minimum capacity of MAX_EPOCH_CREDITS_HISTORY. */
     482           0 :       deq_fd_vote_epoch_credits_t_push_tail(
     483           0 :           epoch_credits,
     484           0 :           ( fd_vote_epoch_credits_t ){
     485           0 :               .epoch = epoch, .credits = credits, .prev_credits = credits } );
     486           0 :     } else {
     487             :       /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L297-L298 */
     488           0 :       deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits )->epoch = epoch;
     489             : 
     490             :       /* Here we can perform the same deque size check and pop if
     491             :          we're beyond the maximum epoch credits len. */
     492           0 :       if( FD_UNLIKELY( deq_fd_vote_epoch_credits_t_cnt( epoch_credits )>MAX_EPOCH_CREDITS_HISTORY ) ) {
     493           0 :         deq_fd_vote_epoch_credits_t_pop_head( epoch_credits );
     494           0 :       }
     495           0 :     }
     496           0 :   }
     497             : 
     498             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L663
     499           0 :   deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits )->credits = fd_ulong_sat_add(
     500           0 :       deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits )->credits, credits );
     501           0 : }
     502             : 
     503             : int
     504             : fd_vsv_process_timestamp( fd_exec_instr_ctx_t *       ctx,
     505             :                           fd_vote_state_versioned_t * self,
     506             :                           ulong                       slot,
     507           0 :                           long                        timestamp ) {
     508             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L160 */
     509           0 :   fd_vote_block_timestamp_t const * last_timestamp = fd_vsv_get_last_timestamp( self );
     510           0 :   if( FD_UNLIKELY(
     511           0 :           ( slot<last_timestamp->slot || timestamp<last_timestamp->timestamp ) ||
     512           0 :           ( slot==last_timestamp->slot &&
     513           0 :             ( slot!=last_timestamp->slot || timestamp!=last_timestamp->timestamp ) &&
     514           0 :             last_timestamp->slot!=0UL ) ) ) {
     515           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_TIMESTAMP_TOO_OLD;
     516           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     517           0 :   }
     518             : 
     519             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L168 */
     520           0 :   fd_vote_block_timestamp_t new_timestamp = {
     521           0 :     .slot = slot,
     522           0 :     .timestamp = timestamp,
     523           0 :   };
     524           0 :   fd_vsv_set_last_timestamp( self, &new_timestamp );
     525           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
     526           0 : }
     527             : 
     528             : void
     529           0 : fd_vsv_pop_expired_votes( fd_vote_state_versioned_t * self, ulong next_vote_slot ) {
     530           0 :   fd_landed_vote_t * votes = fd_vsv_get_votes_mutable( self );
     531             : 
     532           0 :   while( !deq_fd_landed_vote_t_empty( votes ) ) {
     533           0 :     fd_landed_vote_t * vote = deq_fd_landed_vote_t_peek_tail( votes );
     534           0 :     if( !( fd_vote_lockout_is_locked_out_at_slot( &vote->lockout, next_vote_slot ) ) ) {
     535           0 :       deq_fd_landed_vote_t_pop_tail( votes );
     536           0 :     } else {
     537           0 :       break;
     538           0 :     }
     539           0 :   }
     540           0 : }
     541             : 
     542             : void
     543             : fd_vsv_process_next_vote_slot( fd_vote_state_versioned_t * self,
     544             :                                ulong                       next_vote_slot,
     545             :                                ulong                       epoch,
     546           0 :                                ulong                       current_slot ) {
     547           0 :   ulong const * last_voted_slot_ = fd_vsv_get_last_voted_slot( self );
     548           0 :   if( FD_UNLIKELY( last_voted_slot_ && next_vote_slot <= *last_voted_slot_ ) ) return;
     549             : 
     550           0 :   fd_vsv_pop_expired_votes( self, next_vote_slot );
     551             : 
     552           0 :   fd_landed_vote_t * votes = fd_vsv_get_votes_mutable( self );
     553             : 
     554           0 :   fd_landed_vote_t landed_vote = {
     555           0 :     .latency = fd_vote_compute_vote_latency( next_vote_slot, current_slot ),
     556           0 :     .lockout = ( fd_vote_lockout_t ){ .slot = next_vote_slot }
     557           0 :   };
     558             : 
     559             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L623
     560           0 :   if( FD_UNLIKELY( deq_fd_landed_vote_t_cnt( votes ) == MAX_LOCKOUT_HISTORY ) ) {
     561           0 :     ulong            credits     = fd_vote_credits_for_vote_at_index( votes, 0 );
     562           0 :     fd_landed_vote_t landed_vote = deq_fd_landed_vote_t_pop_head( votes );
     563           0 :     fd_vsv_set_root_slot( self, &landed_vote.lockout.slot );
     564             : 
     565           0 :     fd_vsv_increment_credits( self, epoch, credits );
     566           0 :   }
     567             : 
     568             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L634
     569           0 :   deq_fd_landed_vote_t_push_tail_wrap( votes, landed_vote );
     570           0 :   double_lockouts( self );
     571           0 : }
     572             : 
     573             : int
     574           0 : fd_vsv_try_convert_to_v3( fd_vote_state_versioned_t * self ) {
     575           0 :   switch( self->kind ) {
     576             :     /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_versions.rs#L47-L73 */
     577           0 :     case fd_vote_state_versioned_enum_uninitialized: {
     578           0 :       return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
     579           0 :     }
     580             :     /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_versions.rs#L75-L91 */
     581           0 :     case fd_vote_state_versioned_enum_v1_14_11: {
     582           0 :       fd_vote_state_1_14_11_t * state = &self->v1_14_11;
     583             : 
     584             :       /* Temporary to hold v3. Note that since v1_14_11 and v3/v4 use
     585             :          the same underlying types for votes, we can just directly set
     586             :          it and avoid an intermediate call to
     587             :          fd_vote_lockout_landed_votes_from_lockouts. */
     588           0 :       fd_vote_state_v3_t v3 = {
     589           0 :         .node_pubkey           = state->node_pubkey,
     590           0 :         .authorized_withdrawer = state->authorized_withdrawer,
     591           0 :         .commission            = state->commission,
     592           0 :         .votes                 = state->votes,
     593           0 :         .has_root_slot         = state->has_root_slot,
     594           0 :         .root_slot             = state->root_slot,
     595           0 :         .authorized_voters     = state->authorized_voters,
     596           0 :         .prior_voters          = state->prior_voters,
     597           0 :         .epoch_credits         = state->epoch_credits,
     598           0 :         .last_timestamp        = state->last_timestamp
     599           0 :       };
     600             : 
     601             :       /* Emplace new vote state into target */
     602           0 :       self->kind = fd_vote_state_versioned_enum_v3;
     603           0 :       self->v3   = v3;
     604             : 
     605           0 :       return FD_EXECUTOR_INSTR_SUCCESS;
     606           0 :     }
     607             :     /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_versions.rs#L93 */
     608           0 :     case fd_vote_state_versioned_enum_v3:
     609           0 :       return FD_EXECUTOR_INSTR_SUCCESS;
     610             :     /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_versions.rs#L96 */
     611           0 :     case fd_vote_state_versioned_enum_v4:
     612           0 :       return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
     613           0 :     default:
     614           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     615           0 :   }
     616           0 : }
     617             : 
     618             : int
     619             : fd_vsv_try_convert_to_v4( fd_vote_state_versioned_t * self,
     620           0 :                           fd_pubkey_t const *         vote_pubkey ) {
     621           0 :   switch( self->kind ) {
     622             :     /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L971-L974 */
     623           0 :     case fd_vote_state_versioned_enum_uninitialized: {
     624           0 :       return FD_EXECUTOR_INSTR_ERR_UNINITIALIZED_ACCOUNT;
     625           0 :     }
     626             :     /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L975-L989 */
     627           0 :     case fd_vote_state_versioned_enum_v1_14_11: {
     628           0 :       fd_vote_state_1_14_11_t * state = &self->v1_14_11;
     629             : 
     630             :       /* Temporary to hold v4. Note that since v1_14_11 and v3/v4 use
     631             :          the same underlying types for votes, we can just directly set
     632             :          it and avoid an intermediate call to
     633             :          fd_vote_lockout_landed_votes_from_lockouts. */
     634           0 :       fd_vote_state_v4_t v4 = {
     635           0 :         .node_pubkey                      = state->node_pubkey,
     636           0 :         .authorized_withdrawer            = state->authorized_withdrawer,
     637           0 :         .inflation_rewards_collector      = *vote_pubkey,
     638           0 :         .block_revenue_collector          = state->node_pubkey,
     639           0 :         .inflation_rewards_commission_bps = fd_ushort_sat_mul( state->commission, 100 ),
     640           0 :         .block_revenue_commission_bps     = DEFAULT_BLOCK_REVENUE_COMMISSION_BPS,
     641           0 :         .pending_delegator_rewards        = 0,
     642           0 :         .has_bls_pubkey_compressed        = 0,
     643           0 :         .votes                            = state->votes,
     644           0 :         .has_root_slot                    = state->has_root_slot,
     645           0 :         .root_slot                        = state->root_slot,
     646           0 :         .authorized_voters                = state->authorized_voters,
     647           0 :         .epoch_credits                    = state->epoch_credits,
     648           0 :         .last_timestamp                   = state->last_timestamp
     649           0 :       };
     650             : 
     651             :       /* Emplace new vote state into target */
     652           0 :       self->kind = fd_vote_state_versioned_enum_v4;
     653           0 :       self->v4   = v4;
     654             : 
     655           0 :       return FD_EXECUTOR_INSTR_SUCCESS;
     656           0 :     }
     657             :     /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L990-L1004 */
     658           0 :     case fd_vote_state_versioned_enum_v3: {
     659           0 :       fd_vote_state_v3_t * state = &self->v3;
     660           0 :       fd_vote_state_v4_t v4 = {
     661           0 :         .node_pubkey                      = state->node_pubkey,
     662           0 :         .authorized_withdrawer            = state->authorized_withdrawer,
     663           0 :         .inflation_rewards_collector      = *vote_pubkey,
     664           0 :         .block_revenue_collector          = state->node_pubkey,
     665           0 :         .inflation_rewards_commission_bps = fd_ushort_sat_mul( state->commission, 100 ),
     666           0 :         .block_revenue_commission_bps     = DEFAULT_BLOCK_REVENUE_COMMISSION_BPS,
     667           0 :         .pending_delegator_rewards        = 0,
     668           0 :         .has_bls_pubkey_compressed        = 0,
     669           0 :         .votes                            = state->votes,
     670           0 :         .has_root_slot                    = state->has_root_slot,
     671           0 :         .root_slot                        = state->root_slot,
     672           0 :         .authorized_voters                = state->authorized_voters,
     673           0 :         .epoch_credits                    = state->epoch_credits,
     674           0 :         .last_timestamp                   = state->last_timestamp
     675           0 :       };
     676             : 
     677             :       /* Emplace new vote state into target */
     678           0 :       self->kind = fd_vote_state_versioned_enum_v4;
     679           0 :       self->v4   = v4;
     680             : 
     681           0 :       return FD_EXECUTOR_INSTR_SUCCESS;
     682           0 :     }
     683             :     /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L1005 */
     684           0 :     case fd_vote_state_versioned_enum_v4:
     685           0 :       return FD_EXECUTOR_INSTR_SUCCESS;
     686           0 :     default:
     687           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     688           0 :   }
     689           0 : }
     690             : 
     691             : int
     692             : fd_vsv_deinitialize_vote_account_state( fd_exec_instr_ctx_t *   ctx,
     693             :                                         fd_borrowed_account_t * vote_account,
     694           0 :                                         int                     target_version ) {
     695             :   /* V4 clears the entire account data on deinitialize.
     696             :      https://github.com/anza-xyz/agave/blob/v4.1.0-alpha.0/programs/vote/src/vote_state/handler.rs#L366-L377 */
     697           0 :   (void)ctx;
     698           0 :   (void)target_version;
     699           0 :   uchar * data;
     700           0 :   ulong   dlen;
     701           0 :   int rc = fd_borrowed_account_get_data_mut( vote_account, &data, &dlen );
     702           0 :   if( FD_UNLIKELY( rc ) ) return rc;
     703           0 :   fd_memset( data, 0, dlen );
     704           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
     705           0 : }
     706             : 
     707             : int
     708           6 : fd_vsv_is_uninitialized( fd_vote_state_versioned_t * self ) {
     709           6 :   switch( self->kind ) {
     710           6 :     case fd_vote_state_versioned_enum_uninitialized:
     711           6 :       return 1;
     712           0 :     case fd_vote_state_versioned_enum_v1_14_11:
     713           0 :       return fd_authorized_voters_is_empty( &self->v1_14_11.authorized_voters );
     714           0 :     case fd_vote_state_versioned_enum_v3:
     715           0 :       return fd_authorized_voters_is_empty( &self->v3.authorized_voters );
     716           0 :     case fd_vote_state_versioned_enum_v4:
     717           0 :       return 0; // v4 vote states are always initialized
     718           0 :     default:
     719           0 :       FD_LOG_CRIT(( "unsupported vote state versioned kind: %u", self->kind ));
     720           6 :   }
     721           6 : }
     722             : 
     723             : int
     724             : fd_vsv_is_correct_size_and_initialized( uchar const * data,
     725         594 :                                         ulong         data_len ) {
     726         594 :   uint const *  disc_ptr = (uint const *)data; // NOT SAFE TO ACCESS YET!
     727             : 
     728             :   /* VoteStateV4::is_correct_size_and_initialized
     729             :      https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v4.rs#L207-L210 */
     730         594 :   if( FD_LIKELY( data_len==FD_VOTE_STATE_V4_SZ && *disc_ptr==fd_vote_state_versioned_enum_v4 ) ) {
     731         255 :     return 1;
     732         255 :   }
     733             : 
     734             :   /* VoteStateV3::is_correct_size_and_initialized
     735             :      https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v3.rs#L509-L514 */
     736         339 :   if( FD_LIKELY( data_len==FD_VOTE_STATE_V3_SZ &&
     737         339 :                  !fd_mem_iszero( data+VERSION_OFFSET, DEFAULT_PRIOR_VOTERS_OFFSET ) ) ) {
     738         339 :     return 1;
     739         339 :   }
     740             : 
     741             :   /* VoteState1_14_11::is_correct_size_and_initialized
     742             :      https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_1_14_11.rs#L63-L69 */
     743           0 :   if( FD_LIKELY( data_len==FD_VOTE_STATE_V2_SZ &&
     744           0 :                  !fd_mem_iszero( data+VERSION_OFFSET, DEFAULT_PRIOR_VOTERS_OFFSET_1_14_11 ) ) ) {
     745           0 :     return 1;
     746           0 :   }
     747             : 
     748           0 :   return 0;
     749           0 : }
     750             : 
     751             : int
     752             : fd_vsv_is_correct_size_owner_and_init( uchar const * owner,
     753             :                                        uchar const * data,
     754         600 :                                        ulong         data_len ) {
     755         600 :   if( FD_UNLIKELY( memcmp( owner, fd_solana_vote_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
     756           6 :     return 0;
     757           6 :   }
     758             : 
     759         594 :   return fd_vsv_is_correct_size_and_initialized( data, data_len );
     760         600 : }

Generated by: LCOV version 1.14