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: 132 522 25.3 %
Date: 2026-08-14 04:54:57 Functions: 16 37 43.2 %

          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         141 :                   fd_vote_state_versioned_t * versioned ) {
      52         141 :   if( FD_UNLIKELY( !fd_vote_state_versioned_deserialize( versioned, acc->data, acc->data_len ) ) ) {
      53           9 :     return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
      54           9 :   }
      55             : 
      56         132 :   return FD_EXECUTOR_INSTR_SUCCESS;
      57         141 : }
      58             : 
      59             : int
      60             : fd_vsv_deserialize( fd_acc_t const *            acc,
      61         117 :                     fd_vote_state_versioned_t * versioned ) {
      62         117 :   int rc = fd_vsv_get_state( acc, versioned );
      63         117 :   if( FD_UNLIKELY( rc ) ) return rc;
      64             : 
      65         108 :   if( FD_UNLIKELY( versioned->kind==fd_vote_state_versioned_enum_uninitialized ) ) {
      66           3 :     return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
      67           3 :   }
      68             : 
      69         105 :   return FD_EXECUTOR_INSTR_SUCCESS;
      70         108 : }
      71             : 
      72             : fd_pubkey_t const *
      73          63 : fd_vsv_get_authorized_withdrawer( fd_vote_state_versioned_t * self ) {
      74          63 :   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          63 :     case fd_vote_state_versioned_enum_v4:
      80          63 :       return &self->v4.authorized_withdrawer;
      81           0 :     default:
      82           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
      83          63 :   }
      84          63 : }
      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          33 : fd_vsv_get_epoch_credits_mutable( fd_vote_state_versioned_t * self ) {
     179          33 :   switch( self->kind ) {
     180           0 :     case fd_vote_state_versioned_enum_v3:
     181           0 :       return self->v3.epoch_credits;
     182          33 :     case fd_vote_state_versioned_enum_v4:
     183          33 :       return self->v4.epoch_credits;
     184           0 :     default:
     185           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     186          33 :   }
     187          33 : }
     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          72 :                   fd_vote_state_versioned_t * state ) {
     208             :   /* https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L974 */
     209          72 :   uchar * data = NULL;
     210          72 :   ulong   dlen = 0UL;
     211          72 :   int err = fd_borrowed_account_get_data_mut( self, &data, &dlen );
     212          72 :   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          72 :   ulong serialized_size = fd_vote_state_versioned_serialized_size( state );
     221          72 :   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          72 :   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          72 :   return FD_EXECUTOR_INSTR_SUCCESS;
     231          72 : }
     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          48 :                                fd_vote_state_versioned_t * versioned ) {
     237          48 :   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          48 :     case fd_vote_state_versioned_enum_v4:
     241          48 :       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          48 :   }
     245          48 : }
     246             : 
     247             : void
     248             : fd_vsv_set_authorized_withdrawer( fd_vote_state_versioned_t * self,
     249           3 :                                   fd_pubkey_t const *         authorized_withdrawer ) {
     250           3 :   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           3 :     case fd_vote_state_versioned_enum_v4: {
     256           3 :       self->v4.authorized_withdrawer = *authorized_withdrawer;
     257           3 :       break;
     258           0 :     }
     259           0 :     default:
     260           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     261           3 :   }
     262           3 : }
     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_INSTR_SIGNERS_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           6 :                         fd_pubkey_t const *         node_pubkey ) {
     307           6 :   switch( self->kind ) {
     308           0 :     case fd_vote_state_versioned_enum_v3:
     309           0 :       self->v3.node_pubkey = *node_pubkey;
     310           0 :       break;
     311           6 :     case fd_vote_state_versioned_enum_v4:
     312           6 :       self->v4.node_pubkey = *node_pubkey;
     313           6 :       break;
     314           0 :     default:
     315           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     316           6 :   }
     317           6 : }
     318             : 
     319             : void
     320             : fd_vsv_set_block_revenue_collector( fd_vote_state_versioned_t * self,
     321          18 :                                     fd_pubkey_t const *         block_revenue_collector ) {
     322          18 :   switch( self->kind ) {
     323          18 :     case fd_vote_state_versioned_enum_v4:
     324          18 :       self->v4.block_revenue_collector = *block_revenue_collector;
     325          18 :       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          18 :   }
     332          18 : }
     333             : 
     334             : void
     335             : fd_vsv_set_inflation_rewards_collector( fd_vote_state_versioned_t * self,
     336          21 :                                         fd_pubkey_t const *         inflation_rewards_collector ) {
     337          21 :   switch( self->kind ) {
     338          21 :     case fd_vote_state_versioned_enum_v4:
     339          21 :       self->v4.inflation_rewards_collector = *inflation_rewards_collector;
     340          21 :       break;
     341           0 :     case fd_vote_state_versioned_enum_v3:
     342             :       /* No-op for v3 */
     343           0 :       break;
     344           0 :     default:
     345           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     346          21 :   }
     347          21 : }
     348             : 
     349             : void
     350             : fd_vsv_set_commission( fd_vote_state_versioned_t * self,
     351          33 :                        uchar                       commission ) {
     352          33 :   switch( self->kind ) {
     353           0 :     case fd_vote_state_versioned_enum_v3:
     354           0 :       self->v3.commission = commission;
     355           0 :       break;
     356          33 :     case fd_vote_state_versioned_enum_v4:
     357          33 :       self->v4.inflation_rewards_commission_bps = (ushort)( commission*100 );
     358          33 :       break;
     359           0 :     default:
     360           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     361          33 :   }
     362          33 : }
     363             : 
     364             : void
     365             : fd_vsv_set_inflation_rewards_commission_bps( fd_vote_state_versioned_t * self,
     366           3 :                                              ushort                      commission_bps ) {
     367           3 :   switch( self->kind ) {
     368           3 :     case fd_vote_state_versioned_enum_v4:
     369           3 :       self->v4.inflation_rewards_commission_bps = commission_bps;
     370           3 :       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           3 :   }
     377           3 : }
     378             : 
     379             : void
     380             : fd_vsv_set_block_revenue_commission_bps( fd_vote_state_versioned_t * self,
     381           0 :                                          ushort                      commission_bps ) {
     382           0 :   switch( self->kind ) {
     383           0 :     case fd_vote_state_versioned_enum_v4:
     384           0 :       self->v4.block_revenue_commission_bps = commission_bps;
     385           0 :       break;
     386           0 :     case fd_vote_state_versioned_enum_v3:
     387             :       /* No-op for v3 */
     388           0 :       break;
     389           0 :     default:
     390           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     391           0 :   }
     392           0 : }
     393             : 
     394             : void
     395           0 : fd_vsv_set_root_slot( fd_vote_state_versioned_t * self, ulong * root_slot ) {
     396           0 :   switch( self->kind ) {
     397           0 :     case fd_vote_state_versioned_enum_v3:
     398           0 :       self->v3.has_root_slot = (root_slot!=NULL);
     399           0 :       if( FD_LIKELY( root_slot ) ) {
     400           0 :         self->v3.root_slot = *root_slot;
     401           0 :       }
     402           0 :       break;
     403           0 :     case fd_vote_state_versioned_enum_v4:
     404           0 :       self->v4.has_root_slot = (root_slot!=NULL);
     405           0 :       if( FD_LIKELY( root_slot ) ) {
     406           0 :         self->v4.root_slot = *root_slot;
     407           0 :       }
     408           0 :       break;
     409           0 :     default:
     410           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     411           0 :   }
     412           0 : }
     413             : 
     414             : static void
     415             : fd_vsv_set_last_timestamp( fd_vote_state_versioned_t *       self,
     416           0 :                            fd_vote_block_timestamp_t const * last_timestamp ) {
     417           0 :   switch( self->kind ) {
     418           0 :     case fd_vote_state_versioned_enum_v3:
     419           0 :       self->v3.last_timestamp = *last_timestamp;
     420           0 :       break;
     421           0 :     case fd_vote_state_versioned_enum_v4:
     422           0 :       self->v4.last_timestamp = *last_timestamp;
     423           0 :       break;
     424           0 :     default:
     425           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     426           0 :   }
     427           0 : }
     428             : 
     429             : /**********************************************************************/
     430             : /* General functions                                                  */
     431             : /**********************************************************************/
     432             : 
     433             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L855
     434             : static void
     435           0 : double_lockouts( fd_vote_state_versioned_t * self ) {
     436           0 :   fd_landed_vote_t * votes = fd_vsv_get_votes_mutable( self );
     437             : 
     438             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L856
     439           0 :   ulong stack_depth = deq_fd_landed_vote_t_cnt( votes );
     440           0 :   ulong i           = 0;
     441             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L857
     442           0 :   for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( votes );
     443           0 :        !deq_fd_landed_vote_t_iter_done( votes, iter );
     444           0 :        iter = deq_fd_landed_vote_t_iter_next( votes, iter ) ) {
     445           0 :     fd_landed_vote_t * v = deq_fd_landed_vote_t_iter_ele( votes, iter );
     446             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L860
     447           0 :     if( stack_depth >
     448           0 :         fd_ulong_checked_add_expect(
     449           0 :             i,
     450           0 :             (ulong)v->lockout.confirmation_count,
     451           0 :             "`confirmation_count` and tower_size should be bounded by `MAX_LOCKOUT_HISTORY`" ) )
     452           0 :       {
     453             :         // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L864
     454           0 :         fd_vote_lockout_increase_confirmation_count( &v->lockout, 1 );
     455           0 :       }
     456           0 :     i++;
     457           0 :   }
     458           0 : }
     459             : 
     460             : void
     461             : fd_vsv_increment_credits( fd_vote_state_versioned_t * self,
     462             :                           ulong                       epoch,
     463           0 :                           ulong                       credits ) {
     464           0 :   fd_vote_epoch_credits_t * epoch_credits = fd_vsv_get_epoch_credits_mutable( self );
     465             : 
     466             :   /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L286-L305 */
     467           0 :   if( FD_UNLIKELY( deq_fd_vote_epoch_credits_t_empty( epoch_credits ) ) ) {
     468             :     /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L286-L288 */
     469           0 :     deq_fd_vote_epoch_credits_t_push_tail_wrap(
     470           0 :         epoch_credits,
     471           0 :         ( fd_vote_epoch_credits_t ){ .epoch = epoch, .credits = 0, .prev_credits = 0 } );
     472           0 :   } else if( FD_LIKELY( epoch !=
     473           0 :                         deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits )->epoch ) ) {
     474             :     /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L290 */
     475           0 :     fd_vote_epoch_credits_t * last = deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits );
     476             : 
     477           0 :     ulong credits      = last->credits;
     478           0 :     ulong prev_credits = last->prev_credits;
     479             : 
     480             :     /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L292-L299 */
     481           0 :     if( FD_LIKELY( credits!=prev_credits ) ) {
     482           0 :       if( FD_UNLIKELY( deq_fd_vote_epoch_credits_t_cnt( epoch_credits )>=MAX_EPOCH_CREDITS_HISTORY ) ) {
     483             :         /* Although Agave performs a `.remove(0)` AFTER the call to
     484             :           `.push()`, there is an edge case where the epoch credits is
     485             :           full, making the call to `_push_tail()` unsafe. Since Agave's
     486             :           structures are dynamically allocated, it is safe for them to
     487             :           simply call `.push()` and then popping afterwards. We have to
     488             :           reverse the order of operations to maintain correct behavior
     489             :           and avoid overflowing the deque.
     490             :           https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L303 */
     491           0 :         deq_fd_vote_epoch_credits_t_pop_head( epoch_credits );
     492           0 :       }
     493             : 
     494             :       /* This will not fail because we already popped if we're at
     495             :          capacity, since the epoch_credits deque is allocated with a
     496             :          minimum capacity of MAX_EPOCH_CREDITS_HISTORY. */
     497           0 :       deq_fd_vote_epoch_credits_t_push_tail(
     498           0 :           epoch_credits,
     499           0 :           ( fd_vote_epoch_credits_t ){
     500           0 :               .epoch = epoch, .credits = credits, .prev_credits = credits } );
     501           0 :     } else {
     502             :       /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L297-L298 */
     503           0 :       deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits )->epoch = epoch;
     504             : 
     505             :       /* Here we can perform the same deque size check and pop if
     506             :          we're beyond the maximum epoch credits len. */
     507           0 :       if( FD_UNLIKELY( deq_fd_vote_epoch_credits_t_cnt( epoch_credits )>MAX_EPOCH_CREDITS_HISTORY ) ) {
     508           0 :         deq_fd_vote_epoch_credits_t_pop_head( epoch_credits );
     509           0 :       }
     510           0 :     }
     511           0 :   }
     512             : 
     513             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L663
     514           0 :   deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits )->credits = fd_ulong_sat_add(
     515           0 :       deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits )->credits, credits );
     516           0 : }
     517             : 
     518             : int
     519             : fd_vsv_process_timestamp( fd_exec_instr_ctx_t *       ctx,
     520             :                           fd_vote_state_versioned_t * self,
     521             :                           ulong                       slot,
     522           0 :                           long                        timestamp ) {
     523             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L160 */
     524           0 :   fd_vote_block_timestamp_t const * last_timestamp = fd_vsv_get_last_timestamp( self );
     525           0 :   if( FD_UNLIKELY(
     526           0 :           ( slot<last_timestamp->slot || timestamp<last_timestamp->timestamp ) ||
     527           0 :           ( slot==last_timestamp->slot &&
     528           0 :             ( slot!=last_timestamp->slot || timestamp!=last_timestamp->timestamp ) &&
     529           0 :             last_timestamp->slot!=0UL ) ) ) {
     530           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_TIMESTAMP_TOO_OLD;
     531           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     532           0 :   }
     533             : 
     534             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L168 */
     535           0 :   fd_vote_block_timestamp_t new_timestamp = {
     536           0 :     .slot = slot,
     537           0 :     .timestamp = timestamp,
     538           0 :   };
     539           0 :   fd_vsv_set_last_timestamp( self, &new_timestamp );
     540           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
     541           0 : }
     542             : 
     543             : void
     544           0 : fd_vsv_pop_expired_votes( fd_vote_state_versioned_t * self, ulong next_vote_slot ) {
     545           0 :   fd_landed_vote_t * votes = fd_vsv_get_votes_mutable( self );
     546             : 
     547           0 :   while( !deq_fd_landed_vote_t_empty( votes ) ) {
     548           0 :     fd_landed_vote_t * vote = deq_fd_landed_vote_t_peek_tail( votes );
     549           0 :     if( !( fd_vote_lockout_is_locked_out_at_slot( &vote->lockout, next_vote_slot ) ) ) {
     550           0 :       deq_fd_landed_vote_t_pop_tail( votes );
     551           0 :     } else {
     552           0 :       break;
     553           0 :     }
     554           0 :   }
     555           0 : }
     556             : 
     557             : void
     558             : fd_vsv_process_next_vote_slot( fd_vote_state_versioned_t * self,
     559             :                                ulong                       next_vote_slot,
     560             :                                ulong                       epoch,
     561           0 :                                ulong                       current_slot ) {
     562           0 :   ulong const * last_voted_slot_ = fd_vsv_get_last_voted_slot( self );
     563           0 :   if( FD_UNLIKELY( last_voted_slot_ && next_vote_slot <= *last_voted_slot_ ) ) return;
     564             : 
     565           0 :   fd_vsv_pop_expired_votes( self, next_vote_slot );
     566             : 
     567           0 :   fd_landed_vote_t * votes = fd_vsv_get_votes_mutable( self );
     568             : 
     569           0 :   fd_landed_vote_t landed_vote = {
     570           0 :     .latency = fd_vote_compute_vote_latency( next_vote_slot, current_slot ),
     571           0 :     .lockout = ( fd_vote_lockout_t ){ .slot = next_vote_slot }
     572           0 :   };
     573             : 
     574             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L623
     575           0 :   if( FD_UNLIKELY( deq_fd_landed_vote_t_cnt( votes ) == MAX_LOCKOUT_HISTORY ) ) {
     576           0 :     ulong            credits     = fd_vote_credits_for_vote_at_index( votes, 0 );
     577           0 :     fd_landed_vote_t landed_vote = deq_fd_landed_vote_t_pop_head( votes );
     578           0 :     fd_vsv_set_root_slot( self, &landed_vote.lockout.slot );
     579             : 
     580           0 :     fd_vsv_increment_credits( self, epoch, credits );
     581           0 :   }
     582             : 
     583             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L634
     584           0 :   deq_fd_landed_vote_t_push_tail_wrap( votes, landed_vote );
     585           0 :   double_lockouts( self );
     586           0 : }
     587             : 
     588             : int
     589           0 : fd_vsv_try_convert_to_v3( fd_vote_state_versioned_t * self ) {
     590           0 :   switch( self->kind ) {
     591             :     /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_versions.rs#L47-L73 */
     592           0 :     case fd_vote_state_versioned_enum_uninitialized: {
     593           0 :       return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
     594           0 :     }
     595             :     /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_versions.rs#L75-L91 */
     596           0 :     case fd_vote_state_versioned_enum_v1_14_11: {
     597           0 :       fd_vote_state_1_14_11_t * state = &self->v1_14_11;
     598             : 
     599             :       /* Temporary to hold v3. Note that since v1_14_11 and v3/v4 use
     600             :          the same underlying types for votes, we can just directly set
     601             :          it and avoid an intermediate call to
     602             :          fd_vote_lockout_landed_votes_from_lockouts. */
     603           0 :       fd_vote_state_v3_t v3 = {
     604           0 :         .node_pubkey           = state->node_pubkey,
     605           0 :         .authorized_withdrawer = state->authorized_withdrawer,
     606           0 :         .commission            = state->commission,
     607           0 :         .votes                 = state->votes,
     608           0 :         .has_root_slot         = state->has_root_slot,
     609           0 :         .root_slot             = state->root_slot,
     610           0 :         .authorized_voters     = state->authorized_voters,
     611           0 :         .prior_voters          = state->prior_voters,
     612           0 :         .epoch_credits         = state->epoch_credits,
     613           0 :         .last_timestamp        = state->last_timestamp
     614           0 :       };
     615             : 
     616             :       /* Emplace new vote state into target */
     617           0 :       self->kind = fd_vote_state_versioned_enum_v3;
     618           0 :       self->v3   = v3;
     619             : 
     620           0 :       return FD_EXECUTOR_INSTR_SUCCESS;
     621           0 :     }
     622             :     /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_versions.rs#L93 */
     623           0 :     case fd_vote_state_versioned_enum_v3:
     624           0 :       return FD_EXECUTOR_INSTR_SUCCESS;
     625             :     /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_versions.rs#L96 */
     626           0 :     case fd_vote_state_versioned_enum_v4:
     627           0 :       return FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
     628           0 :     default:
     629           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     630           0 :   }
     631           0 : }
     632             : 
     633             : int
     634             : fd_vsv_try_convert_to_v4( fd_vote_state_versioned_t * self,
     635          63 :                           fd_pubkey_t const *         vote_pubkey ) {
     636          63 :   switch( self->kind ) {
     637             :     /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L971-L974 */
     638           0 :     case fd_vote_state_versioned_enum_uninitialized: {
     639           0 :       return FD_EXECUTOR_INSTR_ERR_UNINITIALIZED_ACCOUNT;
     640           0 :     }
     641             :     /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L975-L989 */
     642           0 :     case fd_vote_state_versioned_enum_v1_14_11: {
     643           0 :       fd_vote_state_1_14_11_t * state = &self->v1_14_11;
     644             : 
     645             :       /* Temporary to hold v4. Note that since v1_14_11 and v3/v4 use
     646             :          the same underlying types for votes, we can just directly set
     647             :          it and avoid an intermediate call to
     648             :          fd_vote_lockout_landed_votes_from_lockouts. */
     649           0 :       fd_vote_state_v4_t v4 = {
     650           0 :         .node_pubkey                      = state->node_pubkey,
     651           0 :         .authorized_withdrawer            = state->authorized_withdrawer,
     652           0 :         .inflation_rewards_collector      = *vote_pubkey,
     653           0 :         .block_revenue_collector          = state->node_pubkey,
     654           0 :         .inflation_rewards_commission_bps = fd_ushort_sat_mul( state->commission, 100 ),
     655           0 :         .block_revenue_commission_bps     = DEFAULT_BLOCK_REVENUE_COMMISSION_BPS,
     656           0 :         .pending_delegator_rewards        = 0,
     657           0 :         .has_bls_pubkey_compressed        = 0,
     658           0 :         .votes                            = state->votes,
     659           0 :         .has_root_slot                    = state->has_root_slot,
     660           0 :         .root_slot                        = state->root_slot,
     661           0 :         .authorized_voters                = state->authorized_voters,
     662           0 :         .epoch_credits                    = state->epoch_credits,
     663           0 :         .last_timestamp                   = state->last_timestamp
     664           0 :       };
     665             : 
     666             :       /* Emplace new vote state into target */
     667           0 :       self->kind = fd_vote_state_versioned_enum_v4;
     668           0 :       self->v4   = v4;
     669             : 
     670           0 :       return FD_EXECUTOR_INSTR_SUCCESS;
     671           0 :     }
     672             :     /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L990-L1004 */
     673           3 :     case fd_vote_state_versioned_enum_v3: {
     674           3 :       fd_vote_state_v3_t * state = &self->v3;
     675           3 :       fd_vote_state_v4_t v4 = {
     676           3 :         .node_pubkey                      = state->node_pubkey,
     677           3 :         .authorized_withdrawer            = state->authorized_withdrawer,
     678           3 :         .inflation_rewards_collector      = *vote_pubkey,
     679           3 :         .block_revenue_collector          = state->node_pubkey,
     680           3 :         .inflation_rewards_commission_bps = fd_ushort_sat_mul( state->commission, 100 ),
     681           3 :         .block_revenue_commission_bps     = DEFAULT_BLOCK_REVENUE_COMMISSION_BPS,
     682           3 :         .pending_delegator_rewards        = 0,
     683           3 :         .has_bls_pubkey_compressed        = 0,
     684           3 :         .votes                            = state->votes,
     685           3 :         .has_root_slot                    = state->has_root_slot,
     686           3 :         .root_slot                        = state->root_slot,
     687           3 :         .authorized_voters                = state->authorized_voters,
     688           3 :         .epoch_credits                    = state->epoch_credits,
     689           3 :         .last_timestamp                   = state->last_timestamp
     690           3 :       };
     691             : 
     692             :       /* Emplace new vote state into target */
     693           3 :       self->kind = fd_vote_state_versioned_enum_v4;
     694           3 :       self->v4   = v4;
     695             : 
     696           3 :       return FD_EXECUTOR_INSTR_SUCCESS;
     697           0 :     }
     698             :     /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L1005 */
     699          60 :     case fd_vote_state_versioned_enum_v4:
     700          60 :       return FD_EXECUTOR_INSTR_SUCCESS;
     701           0 :     default:
     702           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     703          63 :   }
     704          63 : }
     705             : 
     706             : int
     707             : fd_vsv_deinitialize_vote_account_state( fd_exec_instr_ctx_t *   ctx,
     708             :                                         fd_borrowed_account_t * vote_account,
     709           0 :                                         int                     target_version ) {
     710             :   /* V4 clears the entire account data on deinitialize.
     711             :      https://github.com/anza-xyz/agave/blob/v4.1.0-alpha.0/programs/vote/src/vote_state/handler.rs#L366-L377 */
     712           0 :   (void)ctx;
     713           0 :   (void)target_version;
     714           0 :   uchar * data;
     715           0 :   ulong   dlen;
     716           0 :   int rc = fd_borrowed_account_get_data_mut( vote_account, &data, &dlen );
     717           0 :   if( FD_UNLIKELY( rc ) ) return rc;
     718           0 :   fd_memset( data, 0, dlen );
     719           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
     720           0 : }
     721             : 
     722             : int
     723          90 : fd_vsv_is_uninitialized( fd_vote_state_versioned_t * self ) {
     724          90 :   switch( self->kind ) {
     725          24 :     case fd_vote_state_versioned_enum_uninitialized:
     726          24 :       return 1;
     727           0 :     case fd_vote_state_versioned_enum_v1_14_11:
     728           0 :       return fd_authorized_voters_is_empty( &self->v1_14_11.authorized_voters );
     729           6 :     case fd_vote_state_versioned_enum_v3:
     730           6 :       return fd_authorized_voters_is_empty( &self->v3.authorized_voters );
     731          60 :     case fd_vote_state_versioned_enum_v4:
     732          60 :       return 0; // v4 vote states are always initialized
     733           0 :     default:
     734           0 :       FD_LOG_CRIT(( "unsupported vote state versioned kind: %u", self->kind ));
     735          90 :   }
     736          90 : }
     737             : 
     738             : int
     739             : fd_vsv_is_correct_size_and_initialized( uchar const * data,
     740         711 :                                         ulong         data_len ) {
     741         711 :   uint const *  disc_ptr = (uint const *)data; // NOT SAFE TO ACCESS YET!
     742             : 
     743             :   /* VoteStateV4::is_correct_size_and_initialized
     744             :      https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v4.rs#L207-L210 */
     745         711 :   if( FD_LIKELY( data_len==FD_VOTE_STATE_V4_SZ && *disc_ptr==fd_vote_state_versioned_enum_v4 ) ) {
     746         711 :     return 1;
     747         711 :   }
     748             : 
     749             :   /* VoteStateV3::is_correct_size_and_initialized
     750             :      https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v3.rs#L509-L514 */
     751           0 :   if( FD_LIKELY( data_len==FD_VOTE_STATE_V3_SZ &&
     752           0 :                  !fd_mem_iszero( data+VERSION_OFFSET, DEFAULT_PRIOR_VOTERS_OFFSET ) ) ) {
     753           0 :     return 1;
     754           0 :   }
     755             : 
     756             :   /* VoteState1_14_11::is_correct_size_and_initialized
     757             :      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 */
     758           0 :   if( FD_LIKELY( data_len==FD_VOTE_STATE_V2_SZ &&
     759           0 :                  !fd_mem_iszero( data+VERSION_OFFSET, DEFAULT_PRIOR_VOTERS_OFFSET_1_14_11 ) ) ) {
     760           0 :     return 1;
     761           0 :   }
     762             : 
     763           0 :   return 0;
     764           0 : }
     765             : 
     766             : int
     767             : fd_vsv_is_correct_size_owner_and_init( uchar const * owner,
     768             :                                        uchar const * data,
     769         717 :                                        ulong         data_len ) {
     770         717 :   if( FD_UNLIKELY( memcmp( owner, fd_solana_vote_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
     771           6 :     return 0;
     772           6 :   }
     773             : 
     774         711 :   return fd_vsv_is_correct_size_and_initialized( data, data_len );
     775         717 : }

Generated by: LCOV version 1.14