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: 53 535 9.9 %
Date: 2026-03-31 06:22:16 Functions: 5 36 13.9 %

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

Generated by: LCOV version 1.14