LCOV - code coverage report
Current view: top level - flamenco/runtime/program/vote - fd_vote_state_v3.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 115 119 96.6 %
Date: 2026-03-19 18:19:27 Functions: 6 6 100.0 %

          Line data    Source code
       1             : #include "fd_vote_state_v3.h"
       2             : #include "fd_authorized_voters.h"
       3             : #include "fd_vote_common.h"
       4             : #include "fd_vote_state_versioned.h"
       5             : #include "../fd_vote_program.h"
       6             : #include "../../fd_runtime.h"
       7             : 
       8             : /* to_vote_state_1_14_11 converts a "v3" vote state object into the
       9             :    older "v1.14.11" version.  This destroys the "v3" object in the
      10             :    process.
      11             :    https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L67 */
      12             : static void
      13             : to_vote_state_1_14_11( fd_vote_state_v3_t *      vote_state,
      14             :                        fd_vote_state_1_14_11_t * vote_state_1_14_11, /* out */
      15          17 :                        uchar *                   vote_lockout_mem ) {
      16          17 :   vote_state_1_14_11->node_pubkey           = vote_state->node_pubkey;            /* copy */
      17          17 :   vote_state_1_14_11->authorized_withdrawer = vote_state->authorized_withdrawer;  /* copy */
      18          17 :   vote_state_1_14_11->commission            = vote_state->commission;             /* copy */
      19             : 
      20             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L72
      21          17 :   if( vote_state->votes ) {
      22          14 :     vote_state_1_14_11->votes = deq_fd_vote_lockout_t_join(
      23          14 :       deq_fd_vote_lockout_t_new( vote_lockout_mem, deq_fd_landed_vote_t_cnt( vote_state->votes ) ) );
      24          14 :     for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( vote_state->votes );
      25         158 :          !deq_fd_landed_vote_t_iter_done( vote_state->votes, iter );
      26         144 :          iter = deq_fd_landed_vote_t_iter_next( vote_state->votes, iter ) ) {
      27         144 :       fd_landed_vote_t const * landed_vote = deq_fd_landed_vote_t_iter_ele_const( vote_state->votes, iter );
      28         144 :       deq_fd_vote_lockout_t_push_tail_wrap( vote_state_1_14_11->votes, landed_vote->lockout );
      29         144 :     }
      30          14 :   }
      31             : 
      32          17 :   vote_state_1_14_11->has_root_slot     = vote_state->has_root_slot;      /* copy */
      33          17 :   vote_state_1_14_11->root_slot         = vote_state->root_slot;          /* copy */
      34          17 :   vote_state_1_14_11->authorized_voters = vote_state->authorized_voters;  /* move */
      35          17 :   vote_state_1_14_11->prior_voters      = vote_state->prior_voters;       /* deep copy */
      36          17 :   vote_state_1_14_11->epoch_credits     = vote_state->epoch_credits;      /* move */
      37          17 :   vote_state_1_14_11->last_timestamp    = vote_state->last_timestamp;     /* deep copy */
      38             : 
      39             :   /* Clear moved objects */
      40          17 :   vote_state->authorized_voters.treap = NULL;
      41          17 :   vote_state->authorized_voters.pool  = NULL;
      42          17 :   vote_state->epoch_credits           = NULL;
      43             : 
      44          17 : }
      45             : 
      46             : void
      47             : fd_vote_program_v3_create_new( fd_vote_init_t * const        vote_init,
      48             :                                fd_sol_sysvar_clock_t const * clock,
      49             :                                uchar *                       authorized_voters_mem,
      50           2 :                                fd_vote_state_versioned_t *   versioned /* out */ ) {
      51           2 :   versioned->discriminant = fd_vote_state_versioned_enum_v3;
      52             : 
      53           2 :   fd_vote_state_v3_t * vote_state      = &versioned->inner.v3;
      54           2 :   vote_state->node_pubkey           = vote_init->node_pubkey;
      55           2 :   vote_state->authorized_voters     = *fd_authorized_voters_new( clock->epoch, &vote_init->authorized_voter, authorized_voters_mem );
      56           2 :   vote_state->authorized_withdrawer = vote_init->authorized_withdrawer;
      57           2 :   vote_state->commission            = vote_init->commission;
      58           2 :   vote_state->prior_voters.idx      = 31;
      59           2 :   vote_state->prior_voters.is_empty = 1;
      60           2 : }
      61             : 
      62             : int
      63             : fd_vote_state_v3_set_vote_account_state( fd_exec_instr_ctx_t const * ctx,
      64             :                                          fd_borrowed_account_t *     vote_account,
      65             :                                          fd_vote_state_versioned_t * versioned,
      66          62 :                                          uchar *                     vote_lockout_mem ) {
      67             :   /* This is a horrible conditional expression in Agave.
      68             :      The terms were broken up into their own variables. */
      69          62 :   fd_vote_state_v3_t * v3_vote_state = &versioned->inner.v3;
      70             : 
      71             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L420-L424 */
      72          62 :   fd_rent_t const * rent               = fd_bank_rent_query( ctx->bank );
      73          62 :   int               resize_needed      = fd_borrowed_account_get_data_len( vote_account ) < FD_VOTE_STATE_V3_SZ;
      74          62 :   int               resize_rent_exempt = fd_rent_exempt_minimum_balance( rent, FD_VOTE_STATE_V3_SZ ) <= fd_borrowed_account_get_lamports( vote_account );
      75             : 
      76             :   /* The resize operation itself is part of the horrible conditional,
      77             :      but behind a short-circuit operator. */
      78          62 :   int resize_failed = 0;
      79          62 :   if( resize_needed && resize_rent_exempt ) {
      80             :     /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L422-L424 */
      81          32 :     resize_failed =
      82          32 :       fd_borrowed_account_set_data_length( vote_account, FD_VOTE_STATE_V3_SZ ) != FD_EXECUTOR_INSTR_SUCCESS;
      83          32 :   }
      84             : 
      85          62 :   if( FD_UNLIKELY( resize_needed && ( !resize_rent_exempt || resize_failed ) ) ) {
      86             :     /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L426-L430 */
      87          17 :     fd_vote_state_versioned_t v1_14_11;
      88          17 :     fd_vote_state_versioned_new_disc( &v1_14_11, fd_vote_state_versioned_enum_v1_14_11 );
      89          17 :     to_vote_state_1_14_11( v3_vote_state, &v1_14_11.inner.v1_14_11, vote_lockout_mem );
      90          17 :     return fd_vsv_set_state( vote_account, &v1_14_11 );
      91          17 :   }
      92             : 
      93             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L432-L433 */
      94          45 :   return fd_vsv_set_state( vote_account, versioned );
      95          62 : }
      96             : 
      97             : int
      98             : fd_vote_state_v3_deserialize( fd_borrowed_account_t const * vote_account,
      99             :                               uchar *                       vote_state_mem,
     100        1022 :                               uchar *                       landed_votes_mem ) {
     101             :   /* deserialize_into_ptr is essentially a call to get_state +
     102             :      try_convert_to_v3. It's written a little more verbosely in Agave
     103             :      as they try to optimize the decoding steps.
     104             :      https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v4.0.4/vote-interface/src/state/vote_state_v3.rs#L162-L202 */
     105        1022 :   int rc = fd_vsv_get_state( vote_account->meta, vote_state_mem );
     106        1022 :   if( FD_UNLIKELY( rc ) ) return rc;
     107             : 
     108             :   /* Unlike vote states v4 decoding, vote state v3 decoding will only
     109             :      pass for v1_14_11 and v3 vote states.
     110             :      https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v5.0.0/vote-interface/src/state/vote_state_v3.rs#L157-L164 */
     111         574 :   fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)vote_state_mem;
     112         574 :   if( FD_UNLIKELY( versioned->discriminant!=fd_vote_state_versioned_enum_v1_14_11 &&
     113         574 :                    versioned->discriminant!=fd_vote_state_versioned_enum_v3 ) ) {
     114         403 :     return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
     115         403 :   }
     116             : 
     117         171 :   return fd_vsv_try_convert_to_v3( versioned, landed_votes_mem );
     118         574 : }
     119             : 
     120             : int
     121             : fd_vote_state_v3_get_and_update_authorized_voter( fd_vote_state_v3_t * self,
     122             :                                                   ulong                current_epoch,
     123          39 :                                                   fd_pubkey_t **       pubkey /* out */ ) {
     124             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L832
     125          39 :   fd_vote_authorized_voter_t * authorized_voter = fd_authorized_voters_get_and_cache_authorized_voter_for_epoch(
     126          39 :       &self->authorized_voters,
     127          39 :       current_epoch
     128          39 :   );
     129             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L835
     130          39 :   if( FD_UNLIKELY( !authorized_voter ) ) return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
     131          37 :   *pubkey = &authorized_voter->pubkey;
     132             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L837
     133          37 :   fd_authorized_voters_purge_authorized_voters( &self->authorized_voters, current_epoch );
     134          37 :   return FD_EXECUTOR_INSTR_SUCCESS;
     135          39 : }
     136             : 
     137             : int
     138             : fd_vote_state_v3_set_new_authorized_voter( fd_exec_instr_ctx_t *              ctx,
     139             :                                            fd_vote_state_v3_t *               self,
     140             :                                            fd_pubkey_t const *                authorized_pubkey,
     141             :                                            ulong                              current_epoch,
     142             :                                            ulong                              target_epoch,
     143             :                                            fd_bls_pubkey_compressed_t const * bls_pubkey,
     144             :                                            int                                authorized_withdrawer_signer,
     145             :                                            fd_pubkey_t const *                signers[ FD_TXN_SIG_MAX ],
     146          31 :                                            ulong                              signers_cnt ) {
     147          31 :   int           rc;
     148          31 :   fd_pubkey_t * epoch_authorized_voter = NULL;
     149             : 
     150             :   /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_state/handler.rs#L287-L292 */
     151          31 :   if( FD_UNLIKELY( bls_pubkey!=NULL ) ) {
     152           0 :     return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
     153           0 :   }
     154             : 
     155             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L778
     156          31 :   rc = fd_vote_state_v3_get_and_update_authorized_voter( self, current_epoch, &epoch_authorized_voter );
     157          31 :   if( FD_UNLIKELY( rc ) ) return rc;
     158             : 
     159             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L779
     160          29 :   rc = fd_vote_signature_verify( epoch_authorized_voter, authorized_withdrawer_signer, signers, signers_cnt );
     161          29 :   if( FD_UNLIKELY( rc ) ) return rc;
     162             : 
     163             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L786
     164          16 :   if( FD_UNLIKELY( fd_authorized_voters_contains( &self->authorized_voters, target_epoch ) ) ) {
     165           1 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_TOO_SOON_TO_REAUTHORIZE;
     166           1 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     167           1 :   }
     168             : 
     169             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L791
     170          15 :   fd_vote_authorized_voter_t * latest_authorized =
     171          15 :       fd_authorized_voters_last( &self->authorized_voters );
     172             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L794
     173          15 :   if( FD_UNLIKELY( ( !latest_authorized ) ) ) return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
     174          15 :   ulong         latest_epoch             = latest_authorized->epoch;
     175          15 :   fd_pubkey_t * latest_authorized_pubkey = &latest_authorized->pubkey;
     176             : 
     177             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L799
     178          15 :   if( !fd_pubkey_eq( latest_authorized_pubkey, authorized_pubkey ) ) {
     179          14 :     fd_vote_prior_voters_t * prior_voters = &self->prior_voters;
     180             : 
     181             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L801
     182          14 :     ulong epoch_of_last_authorized_switch = 0UL;
     183          14 :     if( (!prior_voters->is_empty) & (prior_voters->idx < 32) ) {
     184           1 :       epoch_of_last_authorized_switch = prior_voters->buf[prior_voters->idx].epoch_end;
     185           1 :     }
     186             : 
     187             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L810
     188          14 :     if( target_epoch <= latest_epoch )
     189           5 :       return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
     190             : 
     191             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L815
     192           9 :     prior_voters->idx += 1UL;
     193           9 :     prior_voters->idx %= 32UL;
     194           9 :     prior_voters->buf[prior_voters->idx] =
     195           9 :         ( fd_vote_prior_voter_t ){ .pubkey      = *latest_authorized_pubkey,
     196           9 :                                    .epoch_start = epoch_of_last_authorized_switch,
     197           9 :                                    .epoch_end   = target_epoch };
     198           9 :     prior_voters->is_empty = 0;
     199           9 :   }
     200             : 
     201             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L822
     202          10 :   if( FD_UNLIKELY( !fd_vote_authorized_voters_pool_free( self->authorized_voters.pool ) ) ) {
     203           0 :     FD_LOG_CRIT(( "invariant violation: max authorized voter count of vote account exceeded" ));
     204           0 :   }
     205             : 
     206          10 :   fd_vote_authorized_voter_t * ele =
     207          10 :       fd_vote_authorized_voters_pool_ele_acquire( self->authorized_voters.pool );
     208          10 :   ele->epoch  = target_epoch;
     209          10 :   ele->pubkey = *authorized_pubkey;
     210          10 :   ele->prio   = (ulong)&ele->pubkey;
     211          10 :   fd_vote_authorized_voters_treap_ele_insert(
     212          10 :       self->authorized_voters.treap, ele, self->authorized_voters.pool );
     213             : 
     214          10 :   return 0;
     215          10 : }
     216             : 

Generated by: LCOV version 1.14