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: 183 521 35.1 %
Date: 2026-09-17 04:28:31 Functions: 22 37 59.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          42 : last_lockout( fd_vote_state_versioned_t * self ) {
      29          42 :   fd_landed_vote_t * votes = NULL;
      30          42 :   switch( self->kind ) {
      31           0 :     case fd_vote_state_versioned_enum_v3:
      32           0 :       votes = self->v3.votes;
      33           0 :       break;
      34          42 :     case fd_vote_state_versioned_enum_v4:
      35          42 :       votes = self->v4.votes;
      36          42 :       break;
      37           0 :     default:
      38           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
      39          42 :   }
      40             : 
      41          42 :   if( deq_fd_landed_vote_t_empty( votes ) ) return NULL;
      42          18 :   fd_landed_vote_t * last_vote = deq_fd_landed_vote_t_peek_tail( votes );
      43          18 :   return &last_vote->lockout;
      44          42 : }
      45             : 
      46             : /**********************************************************************/
      47             : /* Getters                                                            */
      48             : /**********************************************************************/
      49             : 
      50             : int
      51             : fd_vsv_get_state( fd_acc_t const *            acc,
      52         186 :                   fd_vote_state_versioned_t * versioned ) {
      53         186 :   if( FD_UNLIKELY( !fd_vote_state_versioned_deserialize( versioned, acc->data, acc->data_len ) ) ) {
      54           9 :     return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
      55           9 :   }
      56             : 
      57         177 :   return FD_EXECUTOR_INSTR_SUCCESS;
      58         186 : }
      59             : 
      60             : int
      61             : fd_vsv_deserialize( fd_acc_t const *            acc,
      62         162 :                     fd_vote_state_versioned_t * versioned ) {
      63         162 :   int rc = fd_vsv_get_state( acc, versioned );
      64         162 :   if( FD_UNLIKELY( rc ) ) return rc;
      65             : 
      66         153 :   if( FD_UNLIKELY( versioned->kind==fd_vote_state_versioned_enum_uninitialized ) ) {
      67           3 :     return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
      68           3 :   }
      69             : 
      70         150 :   return FD_EXECUTOR_INSTR_SUCCESS;
      71         153 : }
      72             : 
      73             : fd_pubkey_t const *
      74          63 : fd_vsv_get_authorized_withdrawer( fd_vote_state_versioned_t * self ) {
      75          63 :   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          63 :     case fd_vote_state_versioned_enum_v4:
      81          63 :       return &self->v4.authorized_withdrawer;
      82           0 :     default:
      83           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
      84          63 :   }
      85          63 : }
      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           0 : fd_vsv_get_epoch_credits( fd_vote_state_versioned_t * self ) {
     101           0 :   return fd_vsv_get_epoch_credits_mutable( self );
     102           0 : }
     103             : 
     104             : fd_landed_vote_t const *
     105           0 : fd_vsv_get_votes( fd_vote_state_versioned_t * self ) {
     106           0 :   return fd_vsv_get_votes_mutable( self );
     107           0 : }
     108             : 
     109             : ulong const *
     110          42 : fd_vsv_get_last_voted_slot( fd_vote_state_versioned_t * self ) {
     111          42 :   fd_vote_lockout_t * last_lockout_ = last_lockout( self );
     112          42 :   if( FD_UNLIKELY( !last_lockout_ ) ) return NULL;
     113          18 :   return &last_lockout_->slot;
     114          42 : }
     115             : 
     116             : ulong const *
     117          18 : fd_vsv_get_root_slot( fd_vote_state_versioned_t * self ) {
     118          18 :   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          18 :     case fd_vote_state_versioned_enum_v4:
     123          18 :       if( !self->v4.has_root_slot ) return NULL;
     124           0 :       return &self->v4.root_slot;
     125           0 :     default:
     126           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     127          18 :   }
     128          18 : }
     129             : 
     130             : fd_vote_block_timestamp_t const *
     131          12 : fd_vsv_get_last_timestamp( fd_vote_state_versioned_t * self ) {
     132          12 :   switch( self->kind ) {
     133           0 :     case fd_vote_state_versioned_enum_v3:
     134           0 :       return &self->v3.last_timestamp;
     135          12 :     case fd_vote_state_versioned_enum_v4:
     136          12 :       return &self->v4.last_timestamp;
     137           0 :     default:
     138           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     139          12 :   }
     140          12 : }
     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           0 : fd_vsv_has_bls_pubkey( fd_vote_state_versioned_t * self ) {
     145           0 :   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           0 :     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           0 :       return !!self->v4.has_bls_pubkey_compressed;
     156           0 :     default:
     157           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     158           0 :   }
     159           0 : }
     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           0 : fd_vsv_get_pending_delegator_rewards( fd_vote_state_versioned_t * self ) {
     164           0 :   switch( self->kind ) {
     165           0 :     case fd_vote_state_versioned_enum_v3:
     166           0 :       return 0UL;
     167           0 :     case fd_vote_state_versioned_enum_v4:
     168           0 :       return self->v4.pending_delegator_rewards;
     169           0 :     default:
     170           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     171           0 :   }
     172           0 : }
     173             : 
     174             : /**********************************************************************/
     175             : /* Mutable getters                                                    */
     176             : /**********************************************************************/
     177             : 
     178             : fd_vote_epoch_credits_t *
     179          57 : fd_vsv_get_epoch_credits_mutable( fd_vote_state_versioned_t * self ) {
     180          57 :   switch( self->kind ) {
     181           0 :     case fd_vote_state_versioned_enum_v3:
     182           0 :       return self->v3.epoch_credits;
     183          57 :     case fd_vote_state_versioned_enum_v4:
     184          57 :       return self->v4.epoch_credits;
     185           0 :     default:
     186           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     187          57 :   }
     188          57 : }
     189             : 
     190             : fd_landed_vote_t *
     191          12 : fd_vsv_get_votes_mutable( fd_vote_state_versioned_t * self ) {
     192          12 :   switch( self->kind ) {
     193           0 :     case fd_vote_state_versioned_enum_v3:
     194           0 :       return self->v3.votes;
     195          12 :     case fd_vote_state_versioned_enum_v4:
     196          12 :       return self->v4.votes;
     197           0 :     default:
     198           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     199          12 :   }
     200          12 : }
     201             : 
     202             : /**********************************************************************/
     203             : /* Setters                                                            */
     204             : /**********************************************************************/
     205             : 
     206             : int
     207             : fd_vsv_set_state( fd_borrowed_account_t *     self,
     208          72 :                   fd_vote_state_versioned_t * state ) {
     209             :   /* https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L974 */
     210          72 :   uchar * data = NULL;
     211          72 :   ulong   dlen = 0UL;
     212          72 :   int err = fd_borrowed_account_get_data_mut( self, &data, &dlen );
     213          72 :   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          72 :   ulong serialized_size = fd_vote_state_versioned_serialized_size( state );
     222          72 :   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          72 :   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          72 :   return FD_EXECUTOR_INSTR_SUCCESS;
     232          72 : }
     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          48 :                                fd_vote_state_versioned_t * versioned ) {
     238          48 :   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          48 :     case fd_vote_state_versioned_enum_v4:
     242          48 :       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          48 :   }
     246          48 : }
     247             : 
     248             : void
     249             : fd_vsv_set_authorized_withdrawer( fd_vote_state_versioned_t * self,
     250           3 :                                   fd_pubkey_t const *         authorized_withdrawer ) {
     251           3 :   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           3 :     case fd_vote_state_versioned_enum_v4: {
     257           3 :       self->v4.authorized_withdrawer = *authorized_withdrawer;
     258           3 :       break;
     259           0 :     }
     260           0 :     default:
     261           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     262           3 :   }
     263           3 : }
     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           0 :                                  ulong                       signers_cnt ) {
     275           0 :   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           0 :     case fd_vote_state_versioned_enum_v4:
     289           0 :       return fd_vote_state_v4_set_new_authorized_voter(
     290           0 :           ctx,
     291           0 :           &self->v4,
     292           0 :           authorized_pubkey,
     293           0 :           current_epoch,
     294           0 :           target_epoch,
     295           0 :           bls_pubkey,
     296           0 :           authorized_withdrawer_signer,
     297           0 :           signers,
     298           0 :           signers_cnt
     299           0 :       );
     300           0 :     default:
     301           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     302           0 :   }
     303           0 : }
     304             : 
     305             : void
     306             : fd_vsv_set_node_pubkey( fd_vote_state_versioned_t * self,
     307           6 :                         fd_pubkey_t const *         node_pubkey ) {
     308           6 :   switch( self->kind ) {
     309           0 :     case fd_vote_state_versioned_enum_v3:
     310           0 :       self->v3.node_pubkey = *node_pubkey;
     311           0 :       break;
     312           6 :     case fd_vote_state_versioned_enum_v4:
     313           6 :       self->v4.node_pubkey = *node_pubkey;
     314           6 :       break;
     315           0 :     default:
     316           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     317           6 :   }
     318           6 : }
     319             : 
     320             : void
     321             : fd_vsv_set_block_revenue_collector( fd_vote_state_versioned_t * self,
     322          18 :                                     fd_pubkey_t const *         block_revenue_collector ) {
     323          18 :   switch( self->kind ) {
     324          18 :     case fd_vote_state_versioned_enum_v4:
     325          18 :       self->v4.block_revenue_collector = *block_revenue_collector;
     326          18 :       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          18 :   }
     333          18 : }
     334             : 
     335             : void
     336             : fd_vsv_set_inflation_rewards_collector( fd_vote_state_versioned_t * self,
     337          21 :                                         fd_pubkey_t const *         inflation_rewards_collector ) {
     338          21 :   switch( self->kind ) {
     339          21 :     case fd_vote_state_versioned_enum_v4:
     340          21 :       self->v4.inflation_rewards_collector = *inflation_rewards_collector;
     341          21 :       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          21 :   }
     348          21 : }
     349             : 
     350             : void
     351             : fd_vsv_set_commission( fd_vote_state_versioned_t * self,
     352          51 :                        uchar                       commission ) {
     353          51 :   switch( self->kind ) {
     354           0 :     case fd_vote_state_versioned_enum_v3:
     355           0 :       self->v3.commission = commission;
     356           0 :       break;
     357          51 :     case fd_vote_state_versioned_enum_v4:
     358          51 :       self->v4.inflation_rewards_commission_bps = (ushort)( commission*100 );
     359          51 :       break;
     360           0 :     default:
     361           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     362          51 :   }
     363          51 : }
     364             : 
     365             : void
     366             : fd_vsv_set_inflation_rewards_commission_bps( fd_vote_state_versioned_t * self,
     367           3 :                                              ushort                      commission_bps ) {
     368           3 :   switch( self->kind ) {
     369           3 :     case fd_vote_state_versioned_enum_v4:
     370           3 :       self->v4.inflation_rewards_commission_bps = commission_bps;
     371           3 :       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           3 :   }
     378           3 : }
     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           6 : fd_vsv_set_root_slot( fd_vote_state_versioned_t * self, ulong * root_slot ) {
     397           6 :   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           6 :     case fd_vote_state_versioned_enum_v4:
     405           6 :       self->v4.has_root_slot = (root_slot!=NULL);
     406           6 :       if( FD_LIKELY( root_slot ) ) {
     407           6 :         self->v4.root_slot = *root_slot;
     408           6 :       }
     409           6 :       break;
     410           0 :     default:
     411           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     412           6 :   }
     413           6 : }
     414             : 
     415             : void
     416             : fd_vsv_set_last_timestamp( fd_vote_state_versioned_t *       self,
     417           0 :                            fd_vote_block_timestamp_t const * last_timestamp ) {
     418           0 :   switch( self->kind ) {
     419           0 :     case fd_vote_state_versioned_enum_v3:
     420           0 :       self->v3.last_timestamp = *last_timestamp;
     421           0 :       break;
     422           0 :     case fd_vote_state_versioned_enum_v4:
     423           0 :       self->v4.last_timestamp = *last_timestamp;
     424           0 :       break;
     425           0 :     default:
     426           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     427           0 :   }
     428           0 : }
     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           0 : double_lockouts( fd_vote_state_versioned_t * self ) {
     437           0 :   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           0 :   ulong stack_depth = deq_fd_landed_vote_t_cnt( votes );
     441           0 :   ulong i           = 0;
     442             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L857
     443           0 :   for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( votes );
     444           0 :        !deq_fd_landed_vote_t_iter_done( votes, iter );
     445           0 :        iter = deq_fd_landed_vote_t_iter_next( votes, iter ) ) {
     446           0 :     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           0 :     if( stack_depth >
     449           0 :         fd_ulong_checked_add_expect(
     450           0 :             i,
     451           0 :             (ulong)v->lockout.confirmation_count,
     452           0 :             "`confirmation_count` and tower_size should be bounded by `MAX_LOCKOUT_HISTORY`" ) )
     453           0 :       {
     454             :         // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L864
     455           0 :         fd_vote_lockout_increase_confirmation_count( &v->lockout, 1 );
     456           0 :       }
     457           0 :     i++;
     458           0 :   }
     459           0 : }
     460             : 
     461             : void
     462             : fd_vsv_increment_credits( fd_vote_state_versioned_t * self,
     463             :                           ulong                       epoch,
     464           0 :                           ulong                       credits ) {
     465           0 :   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           0 :   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           0 :     deq_fd_vote_epoch_credits_t_push_tail_wrap(
     471           0 :         epoch_credits,
     472           0 :         ( fd_vote_epoch_credits_t ){ .epoch = epoch, .credits = 0, .prev_credits = 0 } );
     473           0 :   } else if( FD_LIKELY( epoch !=
     474           0 :                         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           0 :     fd_vote_epoch_credits_t * last = deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits );
     477             : 
     478           0 :     ulong credits      = last->credits;
     479           0 :     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           0 :     if( FD_LIKELY( credits!=prev_credits ) ) {
     483           0 :       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           0 :         deq_fd_vote_epoch_credits_t_pop_head( epoch_credits );
     493           0 :       }
     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           0 :       deq_fd_vote_epoch_credits_t_push_tail(
     499           0 :           epoch_credits,
     500           0 :           ( fd_vote_epoch_credits_t ){
     501           0 :               .epoch = epoch, .credits = credits, .prev_credits = credits } );
     502           0 :     } 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           0 :       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           0 :       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           0 :     }
     512           0 :   }
     513             : 
     514             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L663
     515           0 :   deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits )->credits = fd_ulong_sat_add(
     516           0 :       deq_fd_vote_epoch_credits_t_peek_tail( epoch_credits )->credits, credits );
     517           0 : }
     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           0 :                           long                        timestamp ) {
     524             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L160 */
     525           0 :   fd_vote_block_timestamp_t const * last_timestamp = fd_vsv_get_last_timestamp( self );
     526           0 :   if( FD_UNLIKELY(
     527           0 :           ( slot<last_timestamp->slot || timestamp<last_timestamp->timestamp ) ||
     528           0 :           ( slot==last_timestamp->slot &&
     529           0 :             ( slot!=last_timestamp->slot || timestamp!=last_timestamp->timestamp ) &&
     530           0 :             last_timestamp->slot!=0UL ) ) ) {
     531           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_TIMESTAMP_TOO_OLD;
     532           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     533           0 :   }
     534             : 
     535             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L168 */
     536           0 :   fd_vote_block_timestamp_t new_timestamp = {
     537           0 :     .slot = slot,
     538           0 :     .timestamp = timestamp,
     539           0 :   };
     540           0 :   fd_vsv_set_last_timestamp( self, &new_timestamp );
     541           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
     542           0 : }
     543             : 
     544             : void
     545           0 : fd_vsv_pop_expired_votes( fd_vote_state_versioned_t * self, ulong next_vote_slot ) {
     546           0 :   fd_landed_vote_t * votes = fd_vsv_get_votes_mutable( self );
     547             : 
     548           0 :   while( !deq_fd_landed_vote_t_empty( votes ) ) {
     549           0 :     fd_landed_vote_t * vote = deq_fd_landed_vote_t_peek_tail( votes );
     550           0 :     if( !( fd_vote_lockout_is_locked_out_at_slot( &vote->lockout, next_vote_slot ) ) ) {
     551           0 :       deq_fd_landed_vote_t_pop_tail( votes );
     552           0 :     } else {
     553           0 :       break;
     554           0 :     }
     555           0 :   }
     556           0 : }
     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           0 :                                ulong                       current_slot ) {
     563           0 :   ulong const * last_voted_slot_ = fd_vsv_get_last_voted_slot( self );
     564           0 :   if( FD_UNLIKELY( last_voted_slot_ && next_vote_slot <= *last_voted_slot_ ) ) return;
     565             : 
     566           0 :   fd_vsv_pop_expired_votes( self, next_vote_slot );
     567             : 
     568           0 :   fd_landed_vote_t * votes = fd_vsv_get_votes_mutable( self );
     569             : 
     570           0 :   fd_landed_vote_t landed_vote = {
     571           0 :     .latency = fd_vote_compute_vote_latency( next_vote_slot, current_slot ),
     572           0 :     .lockout = ( fd_vote_lockout_t ){ .slot = next_vote_slot }
     573           0 :   };
     574             : 
     575             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L623
     576           0 :   if( FD_UNLIKELY( deq_fd_landed_vote_t_cnt( votes ) == MAX_LOCKOUT_HISTORY ) ) {
     577           0 :     ulong            credits     = fd_vote_credits_for_vote_at_index( votes, 0 );
     578           0 :     fd_landed_vote_t landed_vote = deq_fd_landed_vote_t_pop_head( votes );
     579           0 :     fd_vsv_set_root_slot( self, &landed_vote.lockout.slot );
     580             : 
     581           0 :     fd_vsv_increment_credits( self, epoch, credits );
     582           0 :   }
     583             : 
     584             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L634
     585           0 :   deq_fd_landed_vote_t_push_tail_wrap( votes, landed_vote );
     586           0 :   double_lockouts( self );
     587           0 : }
     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          63 :                           fd_pubkey_t const *         vote_pubkey ) {
     637          63 :   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           0 :     case fd_vote_state_versioned_enum_v1_14_11: {
     644           0 :       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           0 :       fd_vote_state_v4_t v4 = {
     651           0 :         .node_pubkey                      = state->node_pubkey,
     652           0 :         .authorized_withdrawer            = state->authorized_withdrawer,
     653           0 :         .inflation_rewards_collector      = *vote_pubkey,
     654           0 :         .block_revenue_collector          = state->node_pubkey,
     655           0 :         .inflation_rewards_commission_bps = fd_ushort_sat_mul( state->commission, 100 ),
     656           0 :         .block_revenue_commission_bps     = DEFAULT_BLOCK_REVENUE_COMMISSION_BPS,
     657           0 :         .pending_delegator_rewards        = 0,
     658           0 :         .has_bls_pubkey_compressed        = 0,
     659           0 :         .votes                            = state->votes,
     660           0 :         .has_root_slot                    = state->has_root_slot,
     661           0 :         .root_slot                        = state->root_slot,
     662           0 :         .authorized_voters                = state->authorized_voters,
     663           0 :         .epoch_credits                    = state->epoch_credits,
     664           0 :         .last_timestamp                   = state->last_timestamp
     665           0 :       };
     666             : 
     667             :       /* Emplace new vote state into target */
     668           0 :       self->kind = fd_vote_state_versioned_enum_v4;
     669           0 :       self->v4   = v4;
     670             : 
     671           0 :       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           3 :     case fd_vote_state_versioned_enum_v3: {
     675           3 :       fd_vote_state_v3_t * state = &self->v3;
     676           3 :       fd_vote_state_v4_t v4 = {
     677           3 :         .node_pubkey                      = state->node_pubkey,
     678           3 :         .authorized_withdrawer            = state->authorized_withdrawer,
     679           3 :         .inflation_rewards_collector      = *vote_pubkey,
     680           3 :         .block_revenue_collector          = state->node_pubkey,
     681           3 :         .inflation_rewards_commission_bps = fd_ushort_sat_mul( state->commission, 100 ),
     682           3 :         .block_revenue_commission_bps     = DEFAULT_BLOCK_REVENUE_COMMISSION_BPS,
     683           3 :         .pending_delegator_rewards        = 0,
     684           3 :         .has_bls_pubkey_compressed        = 0,
     685           3 :         .votes                            = state->votes,
     686           3 :         .has_root_slot                    = state->has_root_slot,
     687           3 :         .root_slot                        = state->root_slot,
     688           3 :         .authorized_voters                = state->authorized_voters,
     689           3 :         .epoch_credits                    = state->epoch_credits,
     690           3 :         .last_timestamp                   = state->last_timestamp
     691           3 :       };
     692             : 
     693             :       /* Emplace new vote state into target */
     694           3 :       self->kind = fd_vote_state_versioned_enum_v4;
     695           3 :       self->v4   = v4;
     696             : 
     697           3 :       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          60 :     case fd_vote_state_versioned_enum_v4:
     701          60 :       return FD_EXECUTOR_INSTR_SUCCESS;
     702           0 :     default:
     703           0 :       FD_LOG_CRIT(( "unsupported vote state version: %u", self->kind ));
     704          63 :   }
     705          63 : }
     706             : 
     707             : int
     708             : fd_vsv_deinitialize_vote_account_state( fd_exec_instr_ctx_t *   ctx,
     709             :                                         fd_borrowed_account_t * vote_account,
     710           0 :                                         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           0 :   (void)ctx;
     714           0 :   (void)target_version;
     715           0 :   uchar * data;
     716           0 :   ulong   dlen;
     717           0 :   int rc = fd_borrowed_account_get_data_mut( vote_account, &data, &dlen );
     718           0 :   if( FD_UNLIKELY( rc ) ) return rc;
     719           0 :   fd_memset( data, 0, dlen );
     720           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
     721           0 : }
     722             : 
     723             : int
     724          90 : fd_vsv_is_uninitialized( fd_vote_state_versioned_t * self ) {
     725          90 :   switch( self->kind ) {
     726          24 :     case fd_vote_state_versioned_enum_uninitialized:
     727          24 :       return 1;
     728           0 :     case fd_vote_state_versioned_enum_v1_14_11:
     729           0 :       return fd_authorized_voters_is_empty( &self->v1_14_11.authorized_voters );
     730           6 :     case fd_vote_state_versioned_enum_v3:
     731           6 :       return fd_authorized_voters_is_empty( &self->v3.authorized_voters );
     732          60 :     case fd_vote_state_versioned_enum_v4:
     733          60 :       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          90 :   }
     737          90 : }
     738             : 
     739             : int
     740             : fd_vsv_is_correct_size_and_initialized( uchar const * data,
     741         807 :                                         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         807 :   if( FD_LIKELY( data_len==FD_VOTE_STATE_V4_SZ && FD_LOAD( uint, data )==fd_vote_state_versioned_enum_v4 ) ) {
     745         783 :     return 1;
     746         783 :   }
     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          24 :   if( FD_LIKELY( data_len==FD_VOTE_STATE_V3_SZ &&
     751          24 :                  !fd_mem_iszero( data+VERSION_OFFSET, DEFAULT_PRIOR_VOTERS_OFFSET ) ) ) {
     752          12 :     return 1;
     753          12 :   }
     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          12 :   if( FD_LIKELY( data_len==FD_VOTE_STATE_V2_SZ &&
     758          12 :                  !fd_mem_iszero( data+VERSION_OFFSET, DEFAULT_PRIOR_VOTERS_OFFSET_1_14_11 ) ) ) {
     759           0 :     return 1;
     760           0 :   }
     761             : 
     762          12 :   return 0;
     763          12 : }
     764             : 
     765             : int
     766             : fd_vsv_is_correct_size_owner_and_init( uchar const * owner,
     767             :                                        uchar const * data,
     768         777 :                                        ulong         data_len ) {
     769         777 :   if( FD_UNLIKELY( memcmp( owner, fd_solana_vote_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
     770           6 :     return 0;
     771           6 :   }
     772             : 
     773         771 :   return fd_vsv_is_correct_size_and_initialized( data, data_len );
     774         777 : }

Generated by: LCOV version 1.14