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: 336 521 64.5 %
Date: 2026-10-22 18:10:43 Functions: 32 37 86.5 %

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

Generated by: LCOV version 1.14