LCOV - code coverage report
Current view: top level - flamenco/runtime/program - fd_vote_program.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 1593 0.0 %
Date: 2025-12-04 04:56:06 Functions: 0 59 0.0 %

          Line data    Source code
       1             : #include "fd_vote_program.h"
       2             : #include "../fd_runtime.h"
       3             : #include "../fd_borrowed_account.h"
       4             : #include "../fd_executor.h"
       5             : #include "../fd_pubkey_utils.h"
       6             : #include "../sysvar/fd_sysvar_rent.h"
       7             : #include "../sysvar/fd_sysvar.h"
       8             : #include "../fd_system_ids.h"
       9             : 
      10             : #include <limits.h>
      11             : #include <math.h>
      12             : #include <stdio.h>
      13             : #include <string.h>
      14             : 
      15             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L35
      16           0 : #define MAX_LOCKOUT_HISTORY 31UL
      17             : 
      18             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L36
      19           0 : #define INITIAL_LOCKOUT 2UL
      20             : 
      21             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L36
      22             : #define MAX_EPOCH_CREDITS_HISTORY 64UL
      23             : 
      24             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L42
      25           0 : #define DEFAULT_PRIOR_VOTERS_OFFSET 114
      26             : 
      27             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L45
      28           0 : #define VOTE_CREDITS_GRACE_SLOTS 2
      29             : 
      30             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L48
      31           0 : #define VOTE_CREDITS_MAXIMUM_PER_SLOT 16
      32             : 
      33             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L51
      34             : #define VOTE_CREDITS_MAXIMUM_PER_SLOT_OLD 8
      35             : 
      36             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/clock.rs#L147
      37             : #define SLOT_DEFAULT 0UL
      38             : 
      39             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/clock.rs#L147
      40             : #define SLOT_MAX ULONG_MAX
      41             : 
      42             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L886
      43           0 : #define VERSION_OFFSET (4UL)
      44             : 
      45             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L887
      46             : #define DEFAULT_PRIOR_VOTERS_END (118)
      47             : 
      48             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L6
      49           0 : #define DEFAULT_PRIOR_VOTERS_OFFSET_1_14_11 (82UL)
      50             : 
      51             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L60
      52             : #define DEFAULT_PRIOR_VOTERS_END_1_14_11 (86UL)
      53             : 
      54             : #define ACCOUNTS_MAX 4 /* Vote instructions take in at most 4 accounts */
      55             : 
      56             : #define DEFAULT_COMPUTE_UNITS 2100UL
      57             : 
      58             : /**********************************************************************/
      59             : /* size_of                                                            */
      60             : /**********************************************************************/
      61             : 
      62             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L82
      63             : static inline ulong
      64           0 : size_of_versioned( int is_current ) {
      65           0 :   return fd_ulong_if( is_current, FD_VOTE_STATE_V3_SZ, FD_VOTE_STATE_V2_SZ );
      66           0 : }
      67             : 
      68             : /**********************************************************************/
      69             : /* impl Lockout                                                       */
      70             : /**********************************************************************/
      71             : 
      72             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L104
      73             : static inline ulong
      74           0 : lockout( fd_vote_lockout_t * self ) {
      75             :   /* Confirmation count can never be greater than MAX_LOCKOUT_HISTORY, preventing overflow.
      76             :      Although Agave does not consider overflow, we do for fuzzing conformance. */
      77           0 :   ulong confirmation_count = fd_ulong_min( self->confirmation_count, MAX_LOCKOUT_HISTORY );
      78           0 :   return 1UL<<confirmation_count;
      79           0 : }
      80             : 
      81             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L110
      82             : static inline ulong
      83           0 : last_locked_out_slot( fd_vote_lockout_t * self ) {
      84           0 :   return fd_ulong_sat_add( self->slot, lockout( self ) );
      85           0 : }
      86             : 
      87             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L114
      88             : static inline ulong
      89           0 : is_locked_out_at_slot( fd_vote_lockout_t * self, ulong slot ) {
      90           0 :   return last_locked_out_slot( self ) >= slot;
      91           0 : }
      92             : 
      93             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L122
      94             : static void
      95           0 : increase_confirmation_count( fd_vote_lockout_t * self, uint by ) {
      96           0 :   self->confirmation_count = fd_uint_sat_add( self->confirmation_count, by );
      97           0 : }
      98             : 
      99             : /**********************************************************************/
     100             : /* impl From<VoteState> for VoteState1_14_11                          */
     101             : /**********************************************************************/
     102             : 
     103             : /* from_vote_state_1_14_11 converts a "current" vote state object into
     104             :    the older "v1.14.11" version.  This destroys the "current" object in
     105             :    the process. */
     106             : 
     107             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L67
     108             : static void
     109             : from_vote_state_1_14_11( fd_vote_state_t *         vote_state,
     110             :                          fd_vote_state_1_14_11_t * vote_state_1_14_11, /* out */
     111           0 :                          uchar *                   vote_lockout_mem ) {
     112           0 :   vote_state_1_14_11->node_pubkey           = vote_state->node_pubkey;            /* copy */
     113           0 :   vote_state_1_14_11->authorized_withdrawer = vote_state->authorized_withdrawer;  /* copy */
     114           0 :   vote_state_1_14_11->commission            = vote_state->commission;             /* copy */
     115             : 
     116             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L72
     117           0 :   if( vote_state->votes ) {
     118           0 :     vote_state_1_14_11->votes = deq_fd_vote_lockout_t_join(
     119           0 :       deq_fd_vote_lockout_t_new( vote_lockout_mem, deq_fd_landed_vote_t_cnt( vote_state->votes ) ) );
     120           0 :     for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( vote_state->votes );
     121           0 :          !deq_fd_landed_vote_t_iter_done( vote_state->votes, iter );
     122           0 :          iter = deq_fd_landed_vote_t_iter_next( vote_state->votes, iter ) ) {
     123           0 :       fd_landed_vote_t const * landed_vote = deq_fd_landed_vote_t_iter_ele_const( vote_state->votes, iter );
     124           0 :       deq_fd_vote_lockout_t_push_tail_wrap( vote_state_1_14_11->votes, landed_vote->lockout );
     125           0 :     }
     126           0 :   }
     127             : 
     128           0 :   vote_state_1_14_11->has_root_slot     = vote_state->has_root_slot;      /* copy */
     129           0 :   vote_state_1_14_11->root_slot         = vote_state->root_slot;          /* copy */
     130           0 :   vote_state_1_14_11->authorized_voters = vote_state->authorized_voters;  /* move */
     131           0 :   vote_state_1_14_11->prior_voters      = vote_state->prior_voters;       /* deep copy */
     132           0 :   vote_state_1_14_11->epoch_credits     = vote_state->epoch_credits;      /* move */
     133           0 :   vote_state_1_14_11->last_timestamp    = vote_state->last_timestamp;     /* deep copy */
     134             : 
     135             :   /* Clear moved objects */
     136           0 :   vote_state->authorized_voters.treap = NULL;
     137           0 :   vote_state->authorized_voters.pool  = NULL;
     138           0 :   vote_state->epoch_credits           = NULL;
     139             : 
     140           0 : }
     141             : 
     142             : /**********************************************************************/
     143             : /* impl VoteAccount                                                   */
     144             : /**********************************************************************/
     145             : 
     146             : /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1074 */
     147             : static int
     148             : get_state( fd_txn_account_t const * self,
     149           0 :             uchar *                 res ) {
     150             : 
     151           0 :   fd_bincode_decode_ctx_t decode = {
     152           0 :     .data    = fd_txn_account_get_data( self ),
     153           0 :     .dataend = fd_txn_account_get_data( self ) + fd_txn_account_get_data_len( self ),
     154           0 :   };
     155             : 
     156           0 :   ulong total_sz = 0UL;
     157           0 :   int err = fd_vote_state_versioned_decode_footprint( &decode, &total_sz );
     158           0 :   if( FD_UNLIKELY( err ) ) {
     159           0 :     return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
     160           0 :   }
     161             : 
     162           0 :   FD_TEST( total_sz<=FD_VOTE_STATE_VERSIONED_FOOTPRINT );
     163             : 
     164           0 :   fd_vote_state_versioned_decode( res, &decode );
     165             : 
     166           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
     167             : 
     168           0 : }
     169             : 
     170             : static int
     171             : set_state( fd_borrowed_account_t *     self,
     172           0 :            fd_vote_state_versioned_t * state ) {
     173             :   /* https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L974 */
     174           0 :   uchar * data = NULL;
     175           0 :   ulong   dlen = 0UL;
     176           0 :   int err = fd_borrowed_account_get_data_mut( self, &data, &dlen );
     177           0 :   if( FD_UNLIKELY( err ) ) {
     178           0 :     return err;
     179           0 :   }
     180             : 
     181             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/src/transaction_context.rs#L978
     182           0 :   ulong serialized_size = fd_vote_state_versioned_size( state );
     183           0 :   if( FD_UNLIKELY( serialized_size > dlen ) )
     184           0 :     return FD_EXECUTOR_INSTR_ERR_ACC_DATA_TOO_SMALL;
     185             : 
     186             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/src/transaction_context.rs#L983
     187           0 :   fd_bincode_encode_ctx_t encode =
     188           0 :     { .data    = data,
     189           0 :       .dataend = data + dlen };
     190           0 :   do {
     191           0 :     int err = fd_vote_state_versioned_encode( state, &encode );
     192           0 :     if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "fd_vote_state_versioned_encode failed (%d)", err ));
     193           0 :   } while(0);
     194             : 
     195           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
     196           0 : }
     197             : 
     198             : /**********************************************************************/
     199             : /* impl AuthorizedVoters                                              */
     200             : /**********************************************************************/
     201             : 
     202             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L17
     203             : static fd_vote_authorized_voters_t *
     204             : authorized_voters_new( ulong               epoch,
     205             :                        fd_pubkey_t const * pubkey,
     206           0 :                        uchar *             mem ) {
     207             : 
     208           0 :   FD_SCRATCH_ALLOC_INIT( l, mem );
     209           0 :   fd_vote_authorized_voters_t * authorized_voters = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_align(),       sizeof(fd_vote_authorized_voters_t) );
     210           0 :   void *                        pool_mem          = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_pool_align(),  fd_vote_authorized_voters_pool_footprint( FD_VOTE_AUTHORIZED_VOTERS_MIN ) );
     211           0 :   void *                        treap_mem         = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_treap_align(), fd_vote_authorized_voters_treap_footprint( FD_VOTE_AUTHORIZED_VOTERS_MIN ) );
     212             : 
     213           0 :   authorized_voters->pool  = fd_vote_authorized_voters_pool_join( fd_vote_authorized_voters_pool_new( pool_mem, FD_VOTE_AUTHORIZED_VOTERS_MIN ) );
     214           0 :   authorized_voters->treap = fd_vote_authorized_voters_treap_join( fd_vote_authorized_voters_treap_new( treap_mem, FD_VOTE_AUTHORIZED_VOTERS_MIN ) );
     215           0 :   if( 0 == fd_vote_authorized_voters_pool_free( authorized_voters->pool ) ) {
     216           0 :     FD_LOG_ERR(( "Authorized_voter pool is empty" ));
     217           0 :   }
     218           0 :   fd_vote_authorized_voter_t * ele = fd_vote_authorized_voters_pool_ele_acquire( authorized_voters->pool );
     219           0 :   ele->epoch  = epoch;
     220           0 :   ele->pubkey = *pubkey;
     221           0 :   ele->prio   = (ulong)&ele->pubkey;
     222           0 :   fd_vote_authorized_voters_treap_ele_insert( authorized_voters->treap, ele, authorized_voters->pool );
     223           0 :   return authorized_voters;
     224           0 : }
     225             : 
     226             : // Helper to create an empty AuthorizedVoters structure (for default/uninitialized states)
     227             : static fd_vote_authorized_voters_t *
     228           0 : authorized_voters_new_empty( uchar * mem ) {
     229           0 :   FD_SCRATCH_ALLOC_INIT( l, mem );
     230           0 :   fd_vote_authorized_voters_t * authorized_voters = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_align(),       sizeof(fd_vote_authorized_voters_t) );
     231           0 :   void *                        pool_mem          = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_pool_align(),  fd_vote_authorized_voters_pool_footprint( FD_VOTE_AUTHORIZED_VOTERS_MIN ) );
     232           0 :   void *                        treap_mem         = FD_SCRATCH_ALLOC_APPEND( l, fd_vote_authorized_voters_treap_align(), fd_vote_authorized_voters_treap_footprint( FD_VOTE_AUTHORIZED_VOTERS_MIN ) );
     233             : 
     234           0 :   authorized_voters->pool  = fd_vote_authorized_voters_pool_join( fd_vote_authorized_voters_pool_new( pool_mem, FD_VOTE_AUTHORIZED_VOTERS_MIN ) );
     235           0 :   authorized_voters->treap = fd_vote_authorized_voters_treap_join( fd_vote_authorized_voters_treap_new( treap_mem, FD_VOTE_AUTHORIZED_VOTERS_MIN ) );
     236           0 :   return authorized_voters;
     237           0 : }
     238             : 
     239             : static inline int
     240           0 : authorized_voters_is_empty( fd_vote_authorized_voters_t * self ) {
     241           0 :   return fd_vote_authorized_voters_treap_ele_cnt( self->treap ) == 0;
     242           0 : }
     243             : 
     244             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L80
     245             : static inline int
     246           0 : authorized_voters_contains( fd_vote_authorized_voters_t * self, ulong epoch ) {
     247           0 :   return !!fd_vote_authorized_voters_treap_ele_query( self->treap, epoch, self->pool );
     248           0 : }
     249             : 
     250             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L72
     251             : static inline fd_vote_authorized_voter_t *
     252           0 : authorized_voters_last( fd_vote_authorized_voters_t * self ) {
     253           0 :   fd_vote_authorized_voters_treap_rev_iter_t iter =
     254           0 :       fd_vote_authorized_voters_treap_rev_iter_init( self->treap, self->pool );
     255           0 :   return fd_vote_authorized_voters_treap_rev_iter_ele( iter, self->pool );
     256           0 : }
     257             : 
     258             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L43
     259             : static void
     260             : authorized_voters_purge_authorized_voters( fd_vote_authorized_voters_t * self,
     261           0 :                                            ulong                         current_epoch ) {
     262             : 
     263             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L46
     264           0 :   ulong expired_keys[ FD_VOTE_AUTHORIZED_VOTERS_MIN ];
     265           0 :   ulong key_cnt                                     = 0;
     266           0 :   for( fd_vote_authorized_voters_treap_fwd_iter_t iter =
     267           0 :            fd_vote_authorized_voters_treap_fwd_iter_init( self->treap, self->pool );
     268           0 :        !fd_vote_authorized_voters_treap_fwd_iter_done( iter );
     269           0 :        iter = fd_vote_authorized_voters_treap_fwd_iter_next( iter, self->pool ) ) {
     270           0 :     fd_vote_authorized_voter_t * ele =
     271           0 :         fd_vote_authorized_voters_treap_fwd_iter_ele( iter, self->pool );
     272           0 :     if( ele->epoch < current_epoch ) expired_keys[key_cnt++] = ele->epoch;
     273           0 :   }
     274             : 
     275             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L52
     276           0 :   for( ulong i = 0; i < key_cnt; i++ ) {
     277           0 :     fd_vote_authorized_voter_t * ele =
     278           0 :         fd_vote_authorized_voters_treap_ele_query( self->treap, expired_keys[i], self->pool );
     279           0 :     fd_vote_authorized_voters_treap_ele_remove( self->treap, ele, self->pool );
     280           0 :     fd_vote_authorized_voters_pool_ele_release( self->pool, ele );
     281           0 :   }
     282             : 
     283             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L60
     284           0 :   FD_TEST( !authorized_voters_is_empty( self ) );
     285             : 
     286           0 : }
     287             : 
     288             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L91
     289             : static fd_vote_authorized_voter_t *
     290             : authorized_voters_get_or_calculate_authorized_voter_for_epoch( fd_vote_authorized_voters_t * self,
     291             :                                                                ulong                         epoch,
     292           0 :                                                                int *                         existed ) {
     293           0 :   *existed                                  = 0;
     294           0 :   ulong                        latest_epoch = 0;
     295           0 :   fd_vote_authorized_voter_t * res =
     296           0 :       fd_vote_authorized_voters_treap_ele_query( self->treap, epoch, self->pool );
     297             :   // "predecessor" would be more big-O optimal here, but mirroring labs logic
     298             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L93
     299           0 :   if( FD_UNLIKELY( !res ) ) {
     300           0 :     for( fd_vote_authorized_voters_treap_fwd_iter_t iter =
     301           0 :              fd_vote_authorized_voters_treap_fwd_iter_init( self->treap, self->pool );
     302           0 :          !fd_vote_authorized_voters_treap_fwd_iter_done( iter );
     303           0 :          iter = fd_vote_authorized_voters_treap_fwd_iter_next( iter, self->pool ) ) {
     304           0 :       fd_vote_authorized_voter_t * ele =
     305           0 :           fd_vote_authorized_voters_treap_fwd_iter_ele( iter, self->pool );
     306           0 :       if( ele->epoch < epoch && ( latest_epoch == 0 || ele->epoch > latest_epoch ) ) {
     307           0 :         latest_epoch = ele->epoch;
     308           0 :         res          = ele;
     309           0 :       }
     310           0 :     }
     311           0 :     *existed = 0;
     312           0 :     return res;
     313           0 :   } else {
     314           0 :     *existed = 1;
     315           0 :     return res;
     316           0 :   }
     317           0 :   return res;
     318           0 : }
     319             : 
     320             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L28
     321             : static fd_vote_authorized_voter_t *
     322             : authorized_voters_get_and_cache_authorized_voter_for_epoch( fd_vote_authorized_voters_t * self,
     323           0 :                                                             ulong                         epoch ) {
     324           0 :   int                          existed = 0;
     325             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L29
     326           0 :   fd_vote_authorized_voter_t * res =
     327           0 :       authorized_voters_get_or_calculate_authorized_voter_for_epoch( self, epoch, &existed );
     328           0 :   if( !res ) return NULL;
     329             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L32
     330           0 :   if( !existed ) {
     331             :     /* insert cannot fail because !existed */
     332           0 :     if( 0 == fd_vote_authorized_voters_pool_free( self->pool) ) {
     333           0 :       FD_LOG_ERR(( "Authorized_voter pool is empty" ));
     334           0 :     }
     335           0 :     fd_vote_authorized_voter_t * ele = fd_vote_authorized_voters_pool_ele_acquire( self->pool );
     336           0 :     ele->epoch                       = epoch;
     337           0 :     ele->pubkey                      = res->pubkey;
     338           0 :     ele->prio                        = (ulong)&res->pubkey;
     339             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/authorized_voters.rs#L33
     340           0 :     fd_vote_authorized_voters_treap_ele_insert( self->treap, ele, self->pool );
     341           0 :   }
     342           0 :   return res;
     343           0 : }
     344             : 
     345             : /**********************************************************************/
     346             : /* impl VoteStateVersions                                             */
     347             : /**********************************************************************/
     348             : 
     349             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L66
     350             : static fd_landed_vote_t *
     351             : landed_votes_from_lockouts( fd_vote_lockout_t * lockouts,
     352           0 :                             uchar *             mem ) {
     353           0 :   if( !lockouts ) return NULL;
     354             : 
     355             :   /* Allocate MAX_LOCKOUT_HISTORY (sane case) by default.  In case the
     356             :      vote account is corrupt, allocate as many entries are needed. */
     357             : 
     358           0 :   ulong cnt = deq_fd_vote_lockout_t_cnt( lockouts );
     359           0 :         cnt = fd_ulong_max( cnt, MAX_LOCKOUT_HISTORY );
     360             : 
     361           0 :   fd_landed_vote_t * landed_votes = deq_fd_landed_vote_t_join( deq_fd_landed_vote_t_new( mem, cnt ) );
     362           0 :   if( FD_UNLIKELY( !landed_votes ) ) {
     363           0 :     FD_LOG_CRIT(( "failed to join landed votes" ));
     364           0 :   }
     365             : 
     366           0 :   for( deq_fd_vote_lockout_t_iter_t iter = deq_fd_vote_lockout_t_iter_init( lockouts );
     367           0 :        !deq_fd_vote_lockout_t_iter_done( lockouts, iter );
     368           0 :        iter = deq_fd_vote_lockout_t_iter_next( lockouts, iter ) ) {
     369           0 :     fd_vote_lockout_t const * ele = deq_fd_vote_lockout_t_iter_ele_const( lockouts, iter );
     370             : 
     371           0 :     fd_landed_vote_t * elem = deq_fd_landed_vote_t_push_tail_nocopy( landed_votes );
     372           0 :     fd_landed_vote_new( elem );
     373             : 
     374           0 :     elem->latency                    = 0;
     375           0 :     elem->lockout.slot               = ele->slot;
     376           0 :     elem->lockout.confirmation_count = ele->confirmation_count;
     377           0 :   }
     378             : 
     379           0 :   return landed_votes;
     380           0 : }
     381             : 
     382             : 
     383             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L70
     384             : static inline int
     385           0 : is_uninitialized( fd_vote_state_versioned_t * self ) {
     386           0 :   switch( self->discriminant ) {
     387           0 :   case fd_vote_state_versioned_enum_v0_23_5:;
     388             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L73
     389           0 :     fd_pubkey_t pubkey_default = { 0 };
     390           0 :     return 0 ==
     391           0 :            memcmp( &self->inner.v0_23_5.authorized_voter, &pubkey_default, sizeof( fd_pubkey_t ) );
     392           0 :   case fd_vote_state_versioned_enum_v1_14_11:;
     393           0 :     return authorized_voters_is_empty( &self->inner.v1_14_11.authorized_voters );
     394           0 :   case fd_vote_state_versioned_enum_current:
     395           0 :     return authorized_voters_is_empty( &self->inner.current.authorized_voters );
     396           0 :   default:
     397           0 :     FD_LOG_ERR(( "missing handler or invalid vote state version: %u", self->discriminant ));
     398           0 :   }
     399           0 : }
     400             : 
     401             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L73
     402             : static void
     403             : convert_to_current( fd_vote_state_versioned_t * self,
     404             :                     uchar *                     authorized_voters_mem,
     405           0 :                     uchar *                     landed_votes_mem ) {
     406           0 :   switch( self->discriminant ) {
     407             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L19
     408           0 :   case fd_vote_state_versioned_enum_v0_23_5: {
     409           0 :     fd_vote_state_0_23_5_t * state = &self->inner.v0_23_5;
     410             :     // Check if uninitialized (authorized_voter is all zeros)
     411           0 :     int is_uninitialized = 1;
     412           0 :     for( ulong i = 0; i < sizeof(fd_pubkey_t); i++ ) {
     413           0 :       if( state->authorized_voter.uc[i] != 0 ) {
     414           0 :         is_uninitialized = 0;
     415           0 :         break;
     416           0 :       }
     417           0 :     }
     418             : 
     419           0 :     fd_vote_authorized_voters_t * authorized_voters;
     420           0 :     if( is_uninitialized ) {
     421             :       // Create empty AuthorizedVoters (default), initialized but with no entries
     422           0 :       authorized_voters = authorized_voters_new_empty( authorized_voters_mem );
     423           0 :     } else {
     424           0 :       authorized_voters = authorized_voters_new(
     425           0 :           state->authorized_voter_epoch, &state->authorized_voter, authorized_voters_mem );
     426           0 :     }
     427             : 
     428             :     /* Temporary to hold current */
     429             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L23
     430           0 :     fd_vote_state_t current = {
     431           0 :       .node_pubkey           = state->node_pubkey,            /* copy */
     432           0 :       .authorized_withdrawer = state->authorized_withdrawer,  /* copy */
     433           0 :       .commission            = state->commission,             /* copy */
     434           0 :       .votes                 = landed_votes_from_lockouts( state->votes, landed_votes_mem ),
     435           0 :       .has_root_slot         = state->has_root_slot,  /* copy */
     436           0 :       .root_slot             = state->root_slot,      /* copy */
     437           0 :       .authorized_voters     = *authorized_voters,
     438           0 :       .prior_voters = (fd_vote_prior_voters_t) {
     439           0 :         .idx      = 31UL,
     440           0 :         .is_empty = 1,
     441           0 :       },
     442           0 :       .epoch_credits  = state->epoch_credits,   /* move */
     443           0 :       .last_timestamp = state->last_timestamp,  /* deep copy */
     444           0 :     };
     445             : 
     446             :     /* Move objects */
     447           0 :     state->epoch_credits = NULL;
     448             : 
     449             :     /* Emplace new vote state into target */
     450           0 :     self->discriminant = fd_vote_state_versioned_enum_current;
     451           0 :     self->inner.current = current;
     452             : 
     453           0 :     break;
     454           0 :   }
     455             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_versions.rs#L44
     456           0 :   case fd_vote_state_versioned_enum_v1_14_11: {
     457           0 :     fd_vote_state_1_14_11_t * state = &self->inner.v1_14_11;
     458             : 
     459             :     /* Temporary to hold current */
     460           0 :     fd_vote_state_t current = {
     461           0 :       .node_pubkey            = state->node_pubkey,            /* copy */
     462           0 :       .authorized_withdrawer  = state->authorized_withdrawer,  /* copy */
     463           0 :       .commission             = state->commission,             /* copy */
     464           0 :       .votes                  = landed_votes_from_lockouts( state->votes, landed_votes_mem ),
     465           0 :       .has_root_slot          = state->has_root_slot,          /* copy */
     466           0 :       .root_slot              = state->root_slot,              /* copy */
     467           0 :       .authorized_voters      = state->authorized_voters,      /* move */
     468           0 :       .prior_voters           = state->prior_voters,           /* deep copy */
     469           0 :       .epoch_credits          = state->epoch_credits,          /* move */
     470           0 :       .last_timestamp         = state->last_timestamp          /* deep copy */
     471           0 :     };
     472             : 
     473             :     /* Move objects */
     474           0 :     state->authorized_voters.treap = NULL;
     475           0 :     state->authorized_voters.pool  = NULL;
     476           0 :     state->epoch_credits           = NULL;
     477             : 
     478             :     /* Emplace new vote state into target */
     479           0 :     self->discriminant = fd_vote_state_versioned_enum_current;
     480           0 :     self->inner.current = current;
     481             : 
     482           0 :     break;
     483           0 :   }
     484           0 :   case fd_vote_state_versioned_enum_current:
     485           0 :     break;
     486           0 :   default:
     487           0 :     FD_LOG_ERR(( "unsupported vote state version: %u", self->discriminant ));
     488           0 :   }
     489           0 : }
     490             : 
     491             : /**********************************************************************/
     492             : /* impl VoteState                                                     */
     493             : /**********************************************************************/
     494             : 
     495             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L428
     496             : static void
     497             : vote_state_new( fd_vote_init_t *              vote_init,
     498             :                 fd_sol_sysvar_clock_t const * clock,
     499             :                 uchar *                       authorized_voters_mem,
     500           0 :                 fd_vote_state_t *             vote_state /* out */ ) {
     501           0 :   vote_state->node_pubkey = vote_init->node_pubkey;
     502           0 :   vote_state->authorized_voters = *authorized_voters_new( clock->epoch, &vote_init->authorized_voter, authorized_voters_mem );
     503             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L431
     504           0 :   vote_state->authorized_withdrawer = vote_init->authorized_withdrawer;
     505           0 :   vote_state->commission            = vote_init->commission;
     506             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L434
     507           0 :   vote_state->prior_voters.idx      = 31;
     508           0 :   vote_state->prior_voters.is_empty = 1;
     509           0 : }
     510             : 
     511             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L985
     512             : static inline int
     513             : verify_authorized_signer( fd_pubkey_t const * authorized,
     514           0 :                           fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) {
     515             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L989
     516           0 :   return fd_signers_contains( signers, authorized ) ?
     517           0 :     FD_EXECUTOR_INSTR_SUCCESS :
     518           0 :     FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
     519           0 : }
     520             : 
     521             : // lambda function: https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L873
     522             : static inline int
     523             : verify( fd_pubkey_t *       epoch_authorized_voter,
     524             :         int                 authorized_withdrawer_signer,
     525           0 :         fd_pubkey_t const * signers[static FD_TXN_SIG_MAX] ) {
     526           0 :   if( authorized_withdrawer_signer )
     527           0 :     return 0;
     528           0 :   else
     529           0 :     return verify_authorized_signer( epoch_authorized_voter, signers );
     530           0 : }
     531             : 
     532             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L845
     533             : static void
     534           0 : pop_expired_votes( fd_vote_state_t * self, ulong next_vote_slot ) {
     535           0 :   while( !deq_fd_landed_vote_t_empty( self->votes ) ) {
     536           0 :     fd_landed_vote_t * vote = deq_fd_landed_vote_t_peek_tail( self->votes );
     537           0 :     if( !( is_locked_out_at_slot( &vote->lockout, next_vote_slot ) ) ) {
     538           0 :       deq_fd_landed_vote_t_pop_tail( self->votes );
     539           0 :     } else {
     540           0 :       break;
     541           0 :     }
     542           0 :   }
     543           0 : }
     544             : 
     545             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L855
     546             : static void
     547           0 : double_lockouts( fd_vote_state_t * self ) {
     548             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L856
     549           0 :   ulong stack_depth = deq_fd_landed_vote_t_cnt( self->votes );
     550           0 :   ulong i           = 0;
     551             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L857
     552           0 :   for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( self->votes );
     553           0 :        !deq_fd_landed_vote_t_iter_done( self->votes, iter );
     554           0 :        iter = deq_fd_landed_vote_t_iter_next( self->votes, iter ) ) {
     555           0 :     fd_landed_vote_t * v = deq_fd_landed_vote_t_iter_ele( self->votes, iter );
     556             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L860
     557           0 :     if( stack_depth >
     558           0 :         fd_ulong_checked_add_expect(
     559           0 :             i,
     560           0 :             (ulong)v->lockout.confirmation_count,
     561           0 :             "`confirmation_count` and tower_size should be bounded by `MAX_LOCKOUT_HISTORY`" ) )
     562           0 :       {
     563             :         // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L864
     564           0 :         increase_confirmation_count( &v->lockout, 1 );
     565           0 :       }
     566           0 :     i++;
     567           0 :   }
     568           0 : }
     569             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L668
     570             : static inline uchar
     571           0 : compute_vote_latency( ulong voted_for_slot, ulong current_slot ) {
     572           0 :   return (uchar)fd_ulong_min( fd_ulong_sat_sub( current_slot, voted_for_slot ), UCHAR_MAX );
     573           0 : }
     574             : 
     575             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L673
     576             : static ulong
     577           0 : credits_for_vote_at_index( fd_vote_state_t * self, ulong index ) {
     578             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L679
     579           0 :   fd_landed_vote_t * landed_vote = deq_fd_landed_vote_t_peek_index( self->votes, index );
     580           0 :   ulong              latency     = landed_vote == NULL ? 0 : landed_vote->latency;
     581             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L683
     582           0 :   ulong              max_credits =  VOTE_CREDITS_MAXIMUM_PER_SLOT;
     583             : 
     584             :   // If latency is 0, this means that the Lockout was created and stored from a software version
     585             :   // that did not store vote latencies; in this case, 1 credit is awarded
     586             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L691
     587           0 :   if( FD_UNLIKELY( latency == 0 ) ) {
     588           0 :     return 1;
     589           0 :   }
     590             : 
     591           0 :   ulong diff = 0;
     592           0 :   int   cf   = fd_ulong_checked_sub( latency, VOTE_CREDITS_GRACE_SLOTS, &diff );
     593           0 :   if( cf != 0 || diff == 0 ) {
     594             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L697
     595           0 :     return max_credits;
     596           0 :   }
     597             : 
     598           0 :   ulong credits = 0;
     599           0 :   cf = fd_ulong_checked_sub( max_credits, diff, &credits );
     600           0 :   if( cf != 0 || credits == 0 ) {
     601             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L705
     602           0 :     return 1;
     603           0 :   }
     604             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L707
     605           0 :   return credits;
     606           0 : }
     607             : 
     608             : /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L282-L309 */
     609             : static void
     610           0 : increment_credits( fd_vote_state_t * self, ulong epoch, ulong credits ) {
     611             :   /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L286-L305 */
     612           0 :   if( FD_UNLIKELY( deq_fd_vote_epoch_credits_t_empty( self->epoch_credits ) ) ) {
     613             :     /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L286-L288 */
     614           0 :     deq_fd_vote_epoch_credits_t_push_tail_wrap(
     615           0 :         self->epoch_credits,
     616           0 :         ( fd_vote_epoch_credits_t ){ .epoch = epoch, .credits = 0, .prev_credits = 0 } );
     617           0 :   } else if( FD_LIKELY( epoch !=
     618           0 :                         deq_fd_vote_epoch_credits_t_peek_tail( self->epoch_credits )->epoch ) ) {
     619             :     /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L290 */
     620           0 :     fd_vote_epoch_credits_t * last = deq_fd_vote_epoch_credits_t_peek_tail( self->epoch_credits );
     621             : 
     622           0 :     ulong credits      = last->credits;
     623           0 :     ulong prev_credits = last->prev_credits;
     624             : 
     625             :     /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L292-L299 */
     626           0 :     if( FD_LIKELY( credits!=prev_credits ) ) {
     627           0 :       if( FD_UNLIKELY( deq_fd_vote_epoch_credits_t_cnt( self->epoch_credits )>=MAX_EPOCH_CREDITS_HISTORY ) ) {
     628             :         /* Although Agave performs a `.remove(0)` AFTER the call to
     629             :           `.push()`, there is an edge case where the epoch credits is
     630             :           full, making the call to `_push_tail()` unsafe. Since Agave's
     631             :           structures are dynamically allocated, it is safe for them to
     632             :           simply call `.push()` and then popping afterwards. We have to
     633             :           reverse the order of operations to maintain correct behavior
     634             :           and avoid overflowing the deque.
     635             :           https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L303 */
     636           0 :         deq_fd_vote_epoch_credits_t_pop_head( self->epoch_credits );
     637           0 :       }
     638             : 
     639             :       /* This will not fail because we already popped if we're at
     640             :          capacity, since the epoch_credits deque is allocated with a
     641             :          minimum capacity of MAX_EPOCH_CREDITS_HISTORY. */
     642           0 :       deq_fd_vote_epoch_credits_t_push_tail(
     643           0 :           self->epoch_credits,
     644           0 :           ( fd_vote_epoch_credits_t ){
     645           0 :               .epoch = epoch, .credits = credits, .prev_credits = credits } );
     646           0 :     } else {
     647             :       /* https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v3.0.0/vote-interface/src/state/vote_state_v3.rs#L297-L298 */
     648           0 :       deq_fd_vote_epoch_credits_t_peek_tail( self->epoch_credits )->epoch = epoch;
     649             : 
     650             :       /* Here we can perform the same deque size check and pop if
     651             :          we're beyond the maximum epoch credits len. */
     652           0 :       if( FD_UNLIKELY( deq_fd_vote_epoch_credits_t_cnt( self->epoch_credits )>MAX_EPOCH_CREDITS_HISTORY ) ) {
     653           0 :         deq_fd_vote_epoch_credits_t_pop_head( self->epoch_credits );
     654           0 :       }
     655           0 :     }
     656           0 :   }
     657             : 
     658             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L663
     659           0 :   deq_fd_vote_epoch_credits_t_peek_tail( self->epoch_credits )->credits = fd_ulong_sat_add(
     660           0 :       deq_fd_vote_epoch_credits_t_peek_tail( self->epoch_credits )->credits, credits );
     661           0 : }
     662             : 
     663             : static inline ulong *
     664             : last_voted_slot( fd_vote_state_t * self );
     665             : 
     666             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L595
     667             : static void
     668             : process_next_vote_slot( fd_vote_state_t * self,
     669             :                         ulong             next_vote_slot,
     670             :                         ulong             epoch,
     671           0 :                         ulong             current_slot ) {
     672           0 :   ulong * last_voted_slot_ = last_voted_slot( self );
     673           0 :   if( FD_UNLIKELY( last_voted_slot_ && next_vote_slot <= *last_voted_slot_ ) ) return;
     674             : 
     675           0 :   pop_expired_votes( self, next_vote_slot );
     676             : 
     677           0 :   fd_landed_vote_t landed_vote = { .latency = compute_vote_latency( next_vote_slot, current_slot ),
     678           0 :                                    ( fd_vote_lockout_t ){ .slot = next_vote_slot } };
     679             : 
     680             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L623
     681           0 :   if( FD_UNLIKELY( deq_fd_landed_vote_t_cnt( self->votes ) == MAX_LOCKOUT_HISTORY ) ) {
     682           0 :     ulong            credits     = credits_for_vote_at_index( self, 0 );
     683           0 :     fd_landed_vote_t landed_vote = deq_fd_landed_vote_t_pop_head( self->votes );
     684           0 :     self->has_root_slot = 1;
     685           0 :     self->root_slot     = landed_vote.lockout.slot;
     686             : 
     687           0 :     increment_credits( self, epoch, credits );
     688           0 :   }
     689             : 
     690             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L634
     691           0 :   deq_fd_landed_vote_t_push_tail_wrap( self->votes, landed_vote );
     692           0 :   double_lockouts( self );
     693           0 : }
     694             : 
     695             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L828
     696             : static int
     697             : get_and_update_authorized_voter( fd_vote_state_t *           self,
     698             :                                  ulong                       current_epoch,
     699           0 :                                  fd_pubkey_t **              pubkey /* out */ ) {
     700             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L832
     701           0 :   fd_vote_authorized_voter_t * authorized_voter =
     702           0 :       authorized_voters_get_and_cache_authorized_voter_for_epoch( &self->authorized_voters,
     703           0 :                                                                   current_epoch );
     704             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L835
     705           0 :   if( FD_UNLIKELY( !authorized_voter ) ) return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
     706           0 :   *pubkey = &authorized_voter->pubkey;
     707             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L837
     708           0 :   authorized_voters_purge_authorized_voters( &self->authorized_voters, current_epoch );
     709           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
     710           0 : }
     711             : 
     712             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L768
     713             : static int
     714             : set_new_authorized_voter( fd_vote_state_t *                          self,
     715             :                           fd_pubkey_t const *                        authorized_pubkey,
     716             :                           ulong                                      current_epoch,
     717             :                           ulong                                      target_epoch,
     718             :                           /* "verify" closure */ int                 authorized_withdrawer_signer,
     719             :                           /* "verify" closure */ fd_pubkey_t const * signers[static FD_TXN_SIG_MAX],
     720           0 :                           fd_exec_instr_ctx_t const *                ctx ) {
     721           0 :   int           rc;
     722           0 :   fd_pubkey_t * epoch_authorized_voter = NULL;
     723             : 
     724             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L778
     725           0 :   rc = get_and_update_authorized_voter( self, current_epoch, &epoch_authorized_voter );
     726           0 :   if( FD_UNLIKELY( rc ) ) return rc;
     727             : 
     728             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L779
     729           0 :   rc = verify( epoch_authorized_voter, authorized_withdrawer_signer, signers );
     730           0 :   if( FD_UNLIKELY( rc ) ) return rc;
     731             : 
     732             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L786
     733           0 :   if( FD_UNLIKELY( authorized_voters_contains( &self->authorized_voters, target_epoch ) ) ) {
     734           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_TOO_SOON_TO_REAUTHORIZE;
     735           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     736           0 :   }
     737             : 
     738             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L791
     739           0 :   fd_vote_authorized_voter_t * latest_authorized =
     740           0 :       authorized_voters_last( &self->authorized_voters );
     741             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L794
     742           0 :   if( FD_UNLIKELY( ( !latest_authorized ) ) ) return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
     743           0 :   ulong         latest_epoch             = latest_authorized->epoch;
     744           0 :   fd_pubkey_t * latest_authorized_pubkey = &latest_authorized->pubkey;
     745             : 
     746             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L799
     747           0 :   if( 0 != memcmp( latest_authorized_pubkey, authorized_pubkey, sizeof( fd_pubkey_t ) ) ) {
     748           0 :     fd_vote_prior_voters_t * prior_voters = &self->prior_voters;
     749             : 
     750             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L801
     751           0 :     ulong epoch_of_last_authorized_switch = 0UL;
     752           0 :     if( (!prior_voters->is_empty) & (prior_voters->idx < 32) ) {
     753           0 :       epoch_of_last_authorized_switch = prior_voters->buf[prior_voters->idx].epoch_end;
     754           0 :     }
     755             : 
     756             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L810
     757           0 :     if( target_epoch <= latest_epoch )
     758           0 :       return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
     759             : 
     760             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L815
     761           0 :     prior_voters->idx += 1UL;
     762           0 :     prior_voters->idx %= 32UL;
     763           0 :     prior_voters->buf[prior_voters->idx] =
     764           0 :         ( fd_vote_prior_voter_t ){ .pubkey      = *latest_authorized_pubkey,
     765           0 :                                    .epoch_start = epoch_of_last_authorized_switch,
     766           0 :                                    .epoch_end   = target_epoch };
     767           0 :     prior_voters->is_empty = 0;
     768           0 :   }
     769             : 
     770             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L822
     771           0 :   if( 0 == fd_vote_authorized_voters_pool_free( self->authorized_voters.pool) ) {
     772           0 :     FD_LOG_ERR(( "Authorized_voter pool is empty" ));
     773           0 :   }
     774             : 
     775           0 :   fd_vote_authorized_voter_t * ele =
     776           0 :       fd_vote_authorized_voters_pool_ele_acquire( self->authorized_voters.pool );
     777           0 :   ele->epoch  = target_epoch;
     778           0 :   ele->pubkey = *authorized_pubkey;
     779           0 :   ele->prio   = (ulong)&ele->pubkey;
     780           0 :   fd_vote_authorized_voters_treap_ele_insert(
     781           0 :       self->authorized_voters.treap, ele, self->authorized_voters.pool );
     782             : 
     783           0 :   return 0;
     784           0 : }
     785             : 
     786             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L869
     787             : static int
     788             : process_timestamp( fd_vote_state_t *           self,
     789             :                    ulong                       slot,
     790             :                    long                        timestamp,
     791           0 :                    fd_exec_instr_ctx_t const * ctx ) {
     792           0 :   if( FD_UNLIKELY(
     793           0 :           ( slot < self->last_timestamp.slot || timestamp < self->last_timestamp.timestamp ) ||
     794           0 :           ( slot == self->last_timestamp.slot &&
     795           0 :             ( slot != self->last_timestamp.slot || timestamp != self->last_timestamp.timestamp ) &&
     796           0 :             self->last_timestamp.slot != 0 ) ) ) {
     797           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_TIMESTAMP_TOO_OLD;
     798           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     799           0 :   }
     800           0 :   self->last_timestamp.slot      = slot;
     801           0 :   self->last_timestamp.timestamp = timestamp;
     802             : 
     803           0 :   return 0;
     804           0 : }
     805             : 
     806             : /**********************************************************************/
     807             : /* mod vote_state                                                    */
     808             : /**********************************************************************/
     809             : 
     810             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L166
     811             : __attribute__((warn_unused_result)) static int
     812             : set_vote_account_state( fd_borrowed_account_t *     vote_account,
     813             :                         fd_vote_state_t *           vote_state,
     814             :                         fd_exec_instr_ctx_t const * ctx /* feature_set */,
     815           0 :                         uchar *                     vote_lockout_mem ) {
     816             :   /* This is a horrible conditional expression in Agave.
     817             :       The terms were broken up into their own variables. */
     818             : 
     819           0 :   ulong vsz = size_of_versioned( 1 );
     820             : 
     821             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L175
     822           0 :   fd_rent_t const rent               = fd_sysvar_cache_rent_read_nofail( ctx->sysvar_cache );
     823           0 :   int             resize_needed      = fd_borrowed_account_get_data_len( vote_account ) < vsz;
     824           0 :   int             resize_rent_exempt = fd_rent_exempt_minimum_balance( &rent, vsz ) <= fd_borrowed_account_get_lamports( vote_account );
     825             : 
     826             :   /* The resize operation itself is part of the horrible conditional,
     827             :       but behind a short-circuit operator. */
     828           0 :   int resize_failed = 0;
     829           0 :   if( resize_needed && resize_rent_exempt ) {
     830             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L179
     831           0 :     resize_failed =
     832           0 :       fd_borrowed_account_set_data_length( vote_account, vsz ) != FD_EXECUTOR_INSTR_SUCCESS;
     833           0 :   }
     834             : 
     835           0 :   if( FD_UNLIKELY( resize_needed && ( !resize_rent_exempt || resize_failed ) ) ) {
     836             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L184
     837           0 :     fd_vote_state_versioned_t v1_14_11;
     838           0 :     fd_vote_state_versioned_new_disc( &v1_14_11, fd_vote_state_versioned_enum_v1_14_11 );
     839           0 :     from_vote_state_1_14_11( vote_state, &v1_14_11.inner.v1_14_11, vote_lockout_mem );
     840           0 :     return set_state( vote_account, &v1_14_11 );
     841           0 :   }
     842             : 
     843             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L189
     844             :   // TODO: This is stupid...  optimize this...
     845           0 :   fd_vote_state_versioned_t new_current = { .discriminant = fd_vote_state_versioned_enum_current,
     846           0 :                                             .inner        = { .current = *vote_state } };
     847           0 :   return set_state( vote_account, &new_current );
     848           0 : }
     849             : 
     850             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L727
     851             : static inline fd_vote_lockout_t *
     852           0 : last_lockout( fd_vote_state_t * self ) {
     853           0 :   if( deq_fd_landed_vote_t_empty( self->votes ) ) return NULL;
     854           0 :   fd_landed_vote_t * last_vote = deq_fd_landed_vote_t_peek_tail( self->votes );
     855           0 :   return &last_vote->lockout;
     856           0 : }
     857             : 
     858             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L731
     859             : static inline ulong *
     860           0 : last_voted_slot( fd_vote_state_t * self ) {
     861           0 :   fd_vote_lockout_t * last_lockout_ = last_lockout( self );
     862           0 :   if( FD_UNLIKELY( !last_lockout_ ) ) return NULL;
     863           0 :   return &last_lockout_->slot;
     864           0 : }
     865             : 
     866             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L573
     867             : static uchar
     868           0 : contains_slot( fd_vote_state_t * vote_state, ulong slot ) {
     869             :   /* Logic is copied from slice::binary_search_by() in Rust. While not fully optimized,
     870             :      it aims to achieve fuzzing conformance for both sorted and unsorted inputs. */
     871           0 :   ulong size = deq_fd_landed_vote_t_cnt( vote_state->votes );
     872           0 :   if( FD_UNLIKELY( size==0UL ) ) return 0;
     873             : 
     874           0 :   ulong base = 0UL;
     875           0 :   while( size>1UL ) {
     876           0 :     ulong half = size / 2UL;
     877           0 :     ulong mid = base + half;
     878           0 :     ulong mid_slot = deq_fd_landed_vote_t_peek_index_const( vote_state->votes, mid )->lockout.slot;
     879           0 :     base = (slot<mid_slot) ? base : mid;
     880           0 :     size -= half;
     881           0 :   }
     882             : 
     883           0 :   return deq_fd_landed_vote_t_peek_index_const( vote_state->votes, base )->lockout.slot==slot;
     884           0 : }
     885             : 
     886             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L201
     887             : static int
     888             : check_and_filter_proposed_vote_state( fd_vote_state_t *           vote_state,
     889             :                                       fd_vote_lockout_t *         proposed_lockouts,
     890             :                                       uchar *                     proposed_has_root,
     891             :                                       ulong *                     proposed_root,
     892             :                                       fd_hash_t const *           proposed_hash,
     893             :                                       fd_slot_hash_t const *      slot_hashes, /* deque */
     894           0 :                                       fd_exec_instr_ctx_t const * ctx ) {
     895             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L208
     896           0 :   if( FD_UNLIKELY( deq_fd_vote_lockout_t_empty( proposed_lockouts ) ) ) {
     897           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_EMPTY_SLOTS;
     898           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     899           0 :   }
     900           0 :   fd_landed_vote_t const * last_vote = NULL;
     901           0 :   if( !deq_fd_landed_vote_t_empty( vote_state->votes ) ) {
     902             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L212
     903           0 :     last_vote = deq_fd_landed_vote_t_peek_tail( vote_state->votes );
     904           0 :   }
     905             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L218
     906           0 :   if( FD_LIKELY( last_vote ) ) {
     907           0 :     if( FD_UNLIKELY( deq_fd_vote_lockout_t_peek_tail_const( proposed_lockouts )->slot <=
     908           0 :                      last_vote->lockout.slot ) ) {
     909           0 :       ctx->txn_out->err.custom_err = FD_VOTE_ERR_VOTE_TOO_OLD;
     910           0 :       return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     911           0 :     }
     912           0 :   }
     913             : 
     914             :   /* must be nonempty, checked above */
     915           0 :   ulong last_vote_state_update_slot = deq_fd_vote_lockout_t_peek_tail_const( proposed_lockouts )->slot;
     916             : 
     917             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L224
     918           0 :   if( FD_UNLIKELY( deq_fd_slot_hash_t_empty( slot_hashes ) ) ) {
     919           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_MISMATCH;
     920           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     921           0 :   }
     922             : 
     923             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L227
     924           0 :   ulong earliest_slot_hash_in_history = deq_fd_slot_hash_t_peek_tail_const( slot_hashes )->slot;
     925             : 
     926             :   /* Check if the proposed vote is too old to be in the SlotHash history */
     927             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L230
     928           0 :   if( FD_UNLIKELY( last_vote_state_update_slot < earliest_slot_hash_in_history ) ) {
     929           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_VOTE_TOO_OLD;
     930           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     931           0 :   }
     932             : 
     933             :   /* Check if the proposed root is too old */
     934             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L237
     935           0 :   if( *proposed_has_root ) {
     936           0 :     ulong const proposed_root_ = *proposed_root;
     937             :     /* If the new proposed root `R` is less than the earliest slot hash in the history
     938             :        such that we cannot verify whether the slot was actually was on this fork, set
     939             :        the root to the latest vote in the current vote that's less than R. */
     940             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L242
     941           0 :     if( proposed_root_ < earliest_slot_hash_in_history ) {
     942           0 :       *proposed_has_root = vote_state->has_root_slot;
     943           0 :       *proposed_root     = vote_state->root_slot;
     944           0 :       for( deq_fd_landed_vote_t_iter_t iter =
     945           0 :                deq_fd_landed_vote_t_iter_init_rev( vote_state->votes );
     946           0 :            !deq_fd_landed_vote_t_iter_done_rev( vote_state->votes, iter );
     947           0 :            iter = deq_fd_landed_vote_t_iter_prev( vote_state->votes, iter ) ) {
     948             :         /* Ensure we're iterating from biggest to smallest vote in the
     949             :            current vote state */
     950           0 :         fd_landed_vote_t const * vote = deq_fd_landed_vote_t_iter_ele_const( vote_state->votes, iter );
     951             :         // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L248
     952           0 :         if( vote->lockout.slot <= proposed_root_ ) {
     953           0 :           *proposed_has_root = 1;
     954           0 :           *proposed_root     = vote->lockout.slot;
     955           0 :           break;
     956           0 :         }
     957             : 
     958           0 :       }
     959           0 :     }
     960           0 :   }
     961             : 
     962             :   /* Index into the new proposed vote state's slots, starting with the root if it exists then
     963             :      we use this mutable root to fold checking the root slot into the below loop for performance */
     964           0 :   int   has_root_to_check       = *proposed_has_root;
     965             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L259
     966           0 :   ulong root_to_check           = *proposed_root;
     967           0 :   ulong proposed_lockouts_index = 0UL;
     968           0 :   ulong lockouts_len = deq_fd_vote_lockout_t_cnt( proposed_lockouts );
     969             : 
     970             :   /* Index into the slot_hashes, starting at the oldest known slot hash */
     971             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L264
     972           0 :   ulong slot_hashes_index = deq_fd_slot_hash_t_cnt( slot_hashes );
     973             : 
     974             :   /* proposed_lockouts_indexes_to_filter's size is bounded by the length
     975             :      of the proposed lockouts provided in the instruction data, which is
     976             :      capped at roughly 10KB / sizeof(ulong). */
     977           0 :   ulong proposed_lockouts_indexes_to_filter[ FD_INSTR_DATA_MAX/sizeof(ulong) ];
     978           0 :   ulong filter_index = 0UL;
     979             : 
     980             :   /* Note:
     981             : 
     982             :     1) `vote_state_update.lockouts` is sorted from oldest/smallest vote to newest/largest
     983             :     vote, due to the way votes are applied to the vote state (newest votes
     984             :     pushed to the back).
     985             : 
     986             :     2) Conversely, `slot_hashes` is sorted from newest/largest vote to
     987             :     the oldest/smallest vote.
     988             : 
     989             :     Unlike for vote updates, vote state updates here can't only check votes older than the last vote
     990             :     because have to ensure that every slot is actually part of the history, not just the most
     991             :     recent ones */
     992             : 
     993             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L279
     994           0 :   while( proposed_lockouts_index < lockouts_len && slot_hashes_index > 0 ) {
     995           0 :     ulong proposed_vote_slot =
     996           0 :       fd_ulong_if( has_root_to_check,
     997             :         // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L281
     998           0 :         root_to_check,
     999             :         // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L283
    1000           0 :         deq_fd_vote_lockout_t_peek_index_const( proposed_lockouts,
    1001           0 :           proposed_lockouts_index )
    1002           0 :         ->slot );
    1003             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L285
    1004           0 :     if( !has_root_to_check && proposed_lockouts_index > 0UL &&
    1005           0 :       proposed_vote_slot <=
    1006           0 :       deq_fd_vote_lockout_t_peek_index_const(
    1007           0 :         proposed_lockouts,
    1008           0 :           fd_ulong_checked_sub_expect(
    1009           0 :             proposed_lockouts_index,
    1010           0 :               1,
    1011           0 :               "`proposed_lockouts_index` is positive when checking `SlotsNotOrdered`" ) )
    1012           0 :       ->slot ) {
    1013             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L293
    1014           0 :       ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_NOT_ORDERED;
    1015           0 :       return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1016           0 :     }
    1017             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L295
    1018           0 :     ulong ancestor_slot =
    1019           0 :       deq_fd_slot_hash_t_peek_index_const(
    1020           0 :         slot_hashes,
    1021           0 :           fd_ulong_checked_sub_expect(
    1022           0 :             slot_hashes_index,
    1023           0 :               1UL,
    1024           0 :               "`slot_hashes_index` is positive when computing `ancestor_slot`" ) )
    1025           0 :       ->slot;
    1026             :     /* Find if this slot in the proposed vote state exists in the SlotHashes history
    1027             :        to confirm if it was a valid ancestor on this fork */
    1028             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L303
    1029           0 :     if( proposed_vote_slot < ancestor_slot ) {
    1030           0 :       if( slot_hashes_index == deq_fd_slot_hash_t_cnt( slot_hashes ) ) {
    1031             :         /* The vote slot does not exist in the SlotHashes history because it's too old,
    1032             :            i.e. older than the oldest slot in the history. */
    1033           0 :         if( proposed_vote_slot >= earliest_slot_hash_in_history ) {
    1034           0 :           ctx->txn_out->err.custom_err = 0;
    1035           0 :           return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1036           0 :         }
    1037             :         // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L310
    1038           0 :         if( !contains_slot( vote_state, proposed_vote_slot ) && !has_root_to_check ) {
    1039             :           /* If the vote slot is both:
    1040             :              1) Too old
    1041             :              2) Doesn't already exist in vote state
    1042             :              Then filter it out */
    1043           0 :           proposed_lockouts_indexes_to_filter[filter_index++] = proposed_lockouts_index;        }
    1044             :         // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L318
    1045           0 :         if( has_root_to_check ) {
    1046           0 :           ulong new_proposed_root = root_to_check;
    1047             :           /* 1. Because `root_to_check.is_some()`, then we know that
    1048             :              we haven't checked the root yet in this loop, so
    1049             :              `proposed_vote_slot` == `new_proposed_root` == `vote_state_update.root` */
    1050           0 :           FD_TEST( new_proposed_root == proposed_vote_slot );
    1051             :           /* 2. We know from the assert earlier in the function that
    1052             :              `proposed_vote_slot < earliest_slot_hash_in_history`,
    1053             :              so from 1. we know that `new_proposed_root < earliest_slot_hash_in_history` */
    1054           0 :           if( new_proposed_root >= earliest_slot_hash_in_history ) {
    1055           0 :             ctx->txn_out->err.custom_err = 0;
    1056           0 :             return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1057           0 :           }
    1058             : 
    1059             :           // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L329
    1060           0 :           has_root_to_check = 0;
    1061           0 :           root_to_check     = ULONG_MAX;
    1062           0 :         } else {
    1063             :           // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L331
    1064           0 :           proposed_lockouts_index = fd_ulong_checked_add_expect(
    1065           0 :             proposed_lockouts_index,
    1066           0 :               1,
    1067           0 :               "`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` when "
    1068           0 :               "`proposed_vote_slot` is too old to be in SlotHashes history" );
    1069           0 :         }
    1070           0 :         continue;
    1071           0 :       } else {
    1072             :         /* If the vote slot is new enough to be in the slot history,
    1073             :            but is not part of the slot history, then it must belong to another fork,
    1074             :            which means this vote state update is invalid. */
    1075             :         // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L340
    1076           0 :         if( has_root_to_check ) {
    1077           0 :           ctx->txn_out->err.custom_err = FD_VOTE_ERR_ROOT_ON_DIFFERENT_FORK;
    1078           0 :           return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1079           0 :         } else {
    1080           0 :           ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_MISMATCH;
    1081           0 :           return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1082           0 :         }
    1083           0 :       }
    1084           0 :     } else if( proposed_vote_slot > ancestor_slot ) {
    1085             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L347
    1086             : 
    1087             :       /* Decrement `slot_hashes_index` to find newer slots in the SlotHashes history */
    1088           0 :       slot_hashes_index = fd_ulong_checked_sub_expect(
    1089           0 :         slot_hashes_index,
    1090           0 :           1,
    1091           0 :           "`slot_hashes_index` is positive when finding newer slots in SlotHashes history" );
    1092           0 :       continue;
    1093           0 :     } else {
    1094             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L354
    1095             : 
    1096             :       /* Once the slot in `vote_state_update.lockouts` is found, bump to the next slot
    1097             :          in `vote_state_update.lockouts` and continue. If we were checking the root,
    1098             :          start checking the vote state instead. */
    1099           0 :       if( has_root_to_check ) {
    1100           0 :         has_root_to_check = 0;
    1101           0 :         root_to_check     = ULONG_MAX;
    1102           0 :       } else {
    1103           0 :         proposed_lockouts_index = fd_ulong_checked_add_expect(
    1104           0 :           proposed_lockouts_index,
    1105           0 :             1,
    1106           0 :             "`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` "
    1107           0 :             "when match is found in SlotHashes history" );
    1108           0 :         slot_hashes_index = fd_ulong_checked_sub_expect(
    1109           0 :           slot_hashes_index,
    1110           0 :             1,
    1111           0 :             "`slot_hashes_index` is positive when match is found in SlotHashes history" );
    1112           0 :       }
    1113           0 :     }
    1114           0 :   }
    1115             : 
    1116             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L372
    1117           0 :   if( proposed_lockouts_index != deq_fd_vote_lockout_t_cnt( proposed_lockouts ) ) {
    1118             :     /* The last vote slot in the update did not exist in SlotHashes */
    1119           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_MISMATCH;
    1120           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1121           0 :   }
    1122             : 
    1123             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L401
    1124           0 :   if( memcmp( &deq_fd_slot_hash_t_peek_index_const( slot_hashes, slot_hashes_index )->hash,
    1125           0 :       proposed_hash,
    1126           0 :       sizeof( fd_hash_t ) ) != 0 ) {
    1127             :     /* This means the newest vote in the slot has a match that
    1128             :        doesn't match the expected hash for that slot on this fork */
    1129           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_HASH_MISMATCH;
    1130           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1131           0 :   }
    1132             : 
    1133             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L418
    1134             :   /* Filter out the irrelevant votes */
    1135           0 :   proposed_lockouts_index = 0UL;
    1136           0 :   ulong filter_votes_index = deq_fd_vote_lockout_t_cnt( proposed_lockouts );
    1137             : 
    1138             :   /* We need to iterate backwards here because proposed_lockouts_indexes_to_filter[ i ] is a
    1139             :      strictly increasing value. Forward iterating can lead to the proposed lockout indicies to get
    1140             :      shifted leading to popping the wrong proposed lockouts or out of bounds accessing. We need
    1141             :      to be sure of handling underflow in this case. */
    1142             : 
    1143           0 :   for( ulong i=filter_index; i>0UL && filter_votes_index>0UL; i-- ) {
    1144           0 :     proposed_lockouts_index = i - 1UL;
    1145           0 :     if( FD_UNLIKELY( proposed_lockouts_indexes_to_filter[ proposed_lockouts_index ]>=filter_votes_index ) ) {
    1146           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
    1147           0 :     }
    1148             : 
    1149           0 :     deq_fd_vote_lockout_t_pop_idx_tail( proposed_lockouts, proposed_lockouts_indexes_to_filter[ proposed_lockouts_index ] );
    1150           0 :     filter_votes_index--;
    1151           0 :   }
    1152             : 
    1153           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
    1154           0 : }
    1155             : 
    1156             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L440
    1157             : static int
    1158             : check_slots_are_valid( fd_vote_state_t *        vote_state,
    1159             :                        ulong const *            vote_slots,
    1160             :                        fd_hash_t const *        vote_hash,
    1161             :                        fd_slot_hash_t const *   slot_hashes, /* deque */
    1162           0 :                        fd_exec_instr_ctx_t const * ctx ) {
    1163           0 :   ulong i              = 0;
    1164           0 :   ulong j              = deq_fd_slot_hash_t_cnt( slot_hashes );
    1165           0 :   ulong vote_slots_len = deq_ulong_cnt( vote_slots );
    1166             : 
    1167             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L462
    1168           0 :   while( i < vote_slots_len && j > 0 ) {
    1169           0 :     ulong * last_voted_slot_ = last_voted_slot( vote_state );
    1170           0 :     if( FD_UNLIKELY( last_voted_slot_ &&
    1171           0 :                      *deq_ulong_peek_index_const( vote_slots, i ) <= *last_voted_slot_ ) ) {
    1172             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L469
    1173           0 :       i = fd_ulong_checked_add_expect(
    1174           0 :           i, 1, "`i` is bounded by `MAX_LOCKOUT_HISTORY` when finding larger slots" );
    1175           0 :       continue;
    1176           0 :     }
    1177             : 
    1178             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L476
    1179           0 :     if( FD_UNLIKELY(
    1180           0 :             *deq_ulong_peek_index_const( vote_slots, i ) !=
    1181           0 :             deq_fd_slot_hash_t_peek_index_const( slot_hashes,
    1182           0 :               fd_ulong_checked_sub_expect( j, 1, "`j` is positive" ) )
    1183           0 :                 ->slot ) ) {
    1184           0 :       j = fd_ulong_checked_sub_expect( j, 1, "`j` is positive when finding newer slots" );
    1185           0 :       continue;
    1186           0 :     }
    1187             : 
    1188             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L486
    1189           0 :     i = fd_ulong_checked_add_expect(
    1190           0 :         i, 1, "`i` is bounded by `MAX_LOCKOUT_HISTORY` when hash is found" );
    1191           0 :     j = fd_ulong_checked_sub_expect( j, 1, "`j` is positive when hash is found" );
    1192           0 :   }
    1193             : 
    1194             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L494
    1195           0 :   if( FD_UNLIKELY( j == deq_fd_slot_hash_t_cnt( slot_hashes ) ) ) {
    1196           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_VOTE_TOO_OLD;
    1197           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1198           0 :   }
    1199           0 :   if( FD_UNLIKELY( i != vote_slots_len ) ) {
    1200           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_MISMATCH;
    1201           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1202           0 :   }
    1203             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L514
    1204           0 :   if( FD_UNLIKELY( 0 != memcmp( &deq_fd_slot_hash_t_peek_index_const( slot_hashes, j )->hash,
    1205           0 :                                 vote_hash,
    1206           0 :                                 32UL ) ) ) {
    1207           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_HASH_MISMATCH;
    1208           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1209           0 :   }
    1210           0 :   return 0;
    1211           0 : }
    1212             : 
    1213             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L565
    1214             : static int
    1215             : process_new_vote_state( fd_vote_state_t *           vote_state,
    1216             :                         fd_landed_vote_t *          new_state,
    1217             :                         int                         has_new_root,
    1218             :                         ulong                       new_root,
    1219             :                         int                         has_timestamp,
    1220             :                         long                        timestamp,
    1221             :                         ulong                       epoch,
    1222             :                         ulong                       current_slot,
    1223           0 :                         fd_exec_instr_ctx_t const * ctx /* feature_set */ ) {
    1224           0 :   int rc;
    1225             : 
    1226           0 :   FD_TEST( !deq_fd_landed_vote_t_empty( new_state ) );
    1227             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L575
    1228           0 :   if( FD_UNLIKELY( deq_fd_landed_vote_t_cnt( new_state ) > MAX_LOCKOUT_HISTORY ) ) {
    1229           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_TOO_MANY_VOTES;
    1230           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1231           0 :   };
    1232             : 
    1233             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L579
    1234           0 :   if( FD_UNLIKELY( has_new_root && vote_state->has_root_slot ) ) {
    1235             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L581
    1236           0 :     if( FD_UNLIKELY( new_root < vote_state->root_slot ) ) {
    1237           0 :       ctx->txn_out->err.custom_err = FD_VOTE_ERR_ROOT_ROLL_BACK;
    1238           0 :       return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1239           0 :     }
    1240           0 :   } else if( FD_UNLIKELY( !has_new_root && vote_state->has_root_slot ) ) {
    1241             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L586
    1242           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_ROOT_ROLL_BACK;
    1243           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1244           0 :   } else {
    1245             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L588
    1246             :     /* no-op */
    1247           0 :   }
    1248             : 
    1249           0 :   fd_landed_vote_t * previous_vote = NULL;
    1250           0 :   for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( new_state );
    1251           0 :        !deq_fd_landed_vote_t_iter_done( new_state, iter );
    1252           0 :        iter = deq_fd_landed_vote_t_iter_next( new_state, iter ) ) {
    1253           0 :     fd_landed_vote_t * vote = deq_fd_landed_vote_t_iter_ele( new_state, iter );
    1254           0 :     if( FD_LIKELY( vote->lockout.confirmation_count == 0 ) ) {
    1255           0 :       ctx->txn_out->err.custom_err = FD_VOTE_ERR_ZERO_CONFIRMATIONS;
    1256           0 :       return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1257           0 :     } else if( FD_UNLIKELY( vote->lockout.confirmation_count > MAX_LOCKOUT_HISTORY ) ) {
    1258           0 :       ctx->txn_out->err.custom_err = FD_VOTE_ERR_CONFIRMATION_TOO_LARGE;
    1259           0 :       return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1260           0 :     } else if( FD_LIKELY( has_new_root ) ) {
    1261           0 :       if( FD_UNLIKELY( vote->lockout.slot <= new_root && new_root != SLOT_DEFAULT ) ) {
    1262           0 :         ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOT_SMALLER_THAN_ROOT;
    1263           0 :         return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1264           0 :       }
    1265           0 :     }
    1266             : 
    1267           0 :     if( FD_LIKELY( previous_vote ) ) {
    1268           0 :       if( FD_UNLIKELY( previous_vote->lockout.slot >= vote->lockout.slot ) ) {
    1269           0 :         ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_NOT_ORDERED;
    1270           0 :         return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1271           0 :       } else if( FD_UNLIKELY( previous_vote->lockout.confirmation_count <=
    1272           0 :                               vote->lockout.confirmation_count ) ) {
    1273           0 :         ctx->txn_out->err.custom_err = FD_VOTE_ERR_CONFIRMATIONS_NOT_ORDERED;
    1274           0 :         return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1275           0 :       } else if( FD_UNLIKELY( vote->lockout.slot >
    1276           0 :                               last_locked_out_slot( &previous_vote->lockout ) ) ) {
    1277           0 :         ctx->txn_out->err.custom_err = FD_VOTE_ERR_NEW_VOTE_STATE_LOCKOUT_MISMATCH;
    1278           0 :         return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1279           0 :       }
    1280           0 :     }
    1281           0 :     previous_vote = vote;
    1282           0 :   }
    1283             : 
    1284           0 :   ulong current_vote_state_index = 0;
    1285           0 :   ulong new_vote_state_index     = 0;
    1286             : 
    1287             :   /* Accumulate credits earned by newly rooted slots.  The behavior changes with
    1288             :      timely_vote_credits: prior to this feature, there was a bug that counted a new root slot as 1
    1289             :      credit even if it had never been voted on. timely_vote_credits fixes this bug by only awarding
    1290             :      credits for slots actually voted on and finalized. */
    1291             : 
    1292             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L635
    1293             : 
    1294             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L641
    1295           0 :   ulong earned_credits      = 0;
    1296             : 
    1297           0 :   if( FD_LIKELY( has_new_root ) ) {
    1298           0 :     for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( vote_state->votes );
    1299           0 :          !deq_fd_landed_vote_t_iter_done( vote_state->votes, iter );
    1300           0 :          iter = deq_fd_landed_vote_t_iter_next( vote_state->votes, iter ) ) {
    1301           0 :       fd_landed_vote_t * current_vote = deq_fd_landed_vote_t_iter_ele( vote_state->votes, iter );
    1302             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L647
    1303           0 :       if( FD_UNLIKELY( current_vote->lockout.slot <= new_root ) ) {
    1304             :         // this is safe because we're inside if has_new_root
    1305           0 :         earned_credits = fd_ulong_checked_add_expect(
    1306           0 :             credits_for_vote_at_index( vote_state,
    1307           0 :               current_vote_state_index ),
    1308           0 :             earned_credits,
    1309           0 :             "`earned_credits` does not overflow" );
    1310           0 :         current_vote_state_index = fd_ulong_checked_add_expect(
    1311           0 :             current_vote_state_index,
    1312           0 :             1,
    1313           0 :             "`current_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` "
    1314           0 :             "when processing new root" );
    1315           0 :         continue;
    1316           0 :       }
    1317           0 :       break;
    1318           0 :     }
    1319           0 :   }
    1320             : 
    1321             :   // For any slots newly added to the new vote state, the vote latency of that slot is not provided by the
    1322             :   // vote instruction contents, but instead is computed from the actual latency of the vote
    1323             :   // instruction. This prevents other validators from manipulating their own vote latencies within their vote states
    1324             :   // and forcing the rest of the cluster to accept these possibly fraudulent latency values.  If the
    1325             :   // timly_vote_credits feature is not enabled then vote latency is set to 0 for new votes.
    1326             :   //
    1327             :   // For any slot that is in both the new state and the current state, the vote latency of the new state is taken
    1328             :   // from the current state.
    1329             :   //
    1330             :   // Thus vote latencies are set here for any newly vote-on slots when a vote instruction is received.
    1331             :   // They are copied into the new vote state after every vote for already voted-on slots.
    1332             :   // And when voted-on slots are rooted, the vote latencies stored in the vote state of all the rooted slots is used
    1333             :   // to compute credits earned.
    1334             :   // All validators compute the same vote latencies because all process the same vote instruction at the
    1335             :   // same slot, and the only time vote latencies are ever computed is at the time that their slot is first voted on;
    1336             :   // after that, the latencies are retained unaltered until the slot is rooted.
    1337             : 
    1338             :   // All the votes in our current vote state that are missing from the new vote state
    1339             :   // must have been expired by later votes. Check that the lockouts match this assumption.
    1340             : 
    1341             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L686
    1342           0 :   while( current_vote_state_index < deq_fd_landed_vote_t_cnt( vote_state->votes ) &&
    1343           0 :          new_vote_state_index < deq_fd_landed_vote_t_cnt( new_state ) ) {
    1344           0 :     fd_landed_vote_t * current_vote =
    1345           0 :         deq_fd_landed_vote_t_peek_index( vote_state->votes, current_vote_state_index );
    1346             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L690
    1347           0 :     fd_landed_vote_t * new_vote =
    1348           0 :         deq_fd_landed_vote_t_peek_index( new_state, new_vote_state_index );
    1349             : 
    1350             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L696
    1351           0 :     if( FD_LIKELY( current_vote->lockout.slot < new_vote->lockout.slot ) ) {
    1352             :       /* The agave implementation of calculating the last locked out
    1353             :          slot does not calculate a min between the current vote's
    1354             :          confirmation count and max lockout history. The reason we do
    1355             :          this is to make sure that the fuzzers continue working:
    1356             :          the max lockout history can not be > MAX_LOCKOUT_HISTORY. */
    1357           0 :       ulong confirmation_count   = fd_ulong_min( current_vote->lockout.confirmation_count, MAX_LOCKOUT_HISTORY );
    1358           0 :       ulong last_locked_out_slot = fd_ulong_sat_add( current_vote->lockout.slot,
    1359           0 :                                                      (ulong)pow( INITIAL_LOCKOUT, (double)confirmation_count ) );
    1360             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L697
    1361           0 :       if( last_locked_out_slot >= new_vote->lockout.slot ) {
    1362             :         // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L698
    1363           0 :         ctx->txn_out->err.custom_err = FD_VOTE_ERR_LOCKOUT_CONFLICT;
    1364           0 :         return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1365           0 :       }
    1366             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L700
    1367           0 :       current_vote_state_index =
    1368           0 :           fd_ulong_checked_add_expect( current_vote_state_index,
    1369           0 :                                        1,
    1370           0 :                                        "`current_vote_state_index` is bounded by "
    1371           0 :                                        "`MAX_LOCKOUT_HISTORY` when slot is less than proposed" );
    1372             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L704
    1373           0 :     } else if( FD_UNLIKELY( current_vote->lockout.slot == new_vote->lockout.slot ) ) {
    1374             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L707
    1375           0 :       if( new_vote->lockout.confirmation_count < current_vote->lockout.confirmation_count ) {
    1376           0 :         ctx->txn_out->err.custom_err = FD_VOTE_ERR_CONFIRMATION_ROLL_BACK;
    1377           0 :         return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1378           0 :       }
    1379             : 
    1380             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L712
    1381           0 :       new_vote->latency =
    1382           0 :           deq_fd_landed_vote_t_peek_index( vote_state->votes, current_vote_state_index )->latency;
    1383             : 
    1384             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L714
    1385           0 :       current_vote_state_index =
    1386           0 :           fd_ulong_checked_add_expect( current_vote_state_index,
    1387           0 :                                        1,
    1388           0 :                                        "`current_vote_state_index` is bounded by "
    1389           0 :                                        "`MAX_LOCKOUT_HISTORY` when slot is equal to proposed" );
    1390             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L717
    1391           0 :       new_vote_state_index =
    1392           0 :           fd_ulong_checked_add_expect( new_vote_state_index,
    1393           0 :                                        1,
    1394           0 :                                        "`new_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` "
    1395           0 :                                        "when slot is equal to proposed" );
    1396           0 :     } else {
    1397             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L722
    1398           0 :       new_vote_state_index =
    1399           0 :           fd_ulong_checked_add_expect( new_vote_state_index,
    1400           0 :                                        1,
    1401           0 :                                        "`new_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` "
    1402           0 :                                        "when slot is greater than proposed" );
    1403           0 :     }
    1404           0 :   }
    1405             : 
    1406             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L737
    1407           0 :   for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( new_state );
    1408           0 :         !deq_fd_landed_vote_t_iter_done( new_state, iter );
    1409           0 :         iter = deq_fd_landed_vote_t_iter_next( new_state, iter ) ) {
    1410           0 :     fd_landed_vote_t * new_vote = deq_fd_landed_vote_t_iter_ele( new_state, iter );
    1411             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L738
    1412           0 :     if( FD_UNLIKELY( new_vote->latency == 0 ) ) {
    1413             :       // this is unlikely because as validators upgrade, it should converge to the new vote state
    1414             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L739
    1415           0 :       new_vote->latency = compute_vote_latency( new_vote->lockout.slot, current_slot );
    1416           0 :     }
    1417           0 :   }
    1418             : 
    1419             :   // doesn't matter what the value of slot if `is_some = 0` i.e. `Option::None`
    1420             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L744
    1421           0 :   int both_none = !vote_state->has_root_slot && !has_new_root;
    1422           0 :   if( ( !both_none && ( vote_state->has_root_slot != has_new_root ||
    1423           0 :                         vote_state->root_slot != new_root ) ) ) {
    1424           0 :     increment_credits( vote_state, epoch, earned_credits );
    1425           0 :   }
    1426             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L750
    1427           0 :   if( FD_LIKELY( has_timestamp ) ) {
    1428             :     /* new_state asserted nonempty at function beginning */
    1429           0 :     if( deq_fd_landed_vote_t_empty( new_state ) ) {
    1430           0 :       FD_LOG_ERR(( "solana panic" ));
    1431             :       // TODO: solana panics ...  unclear what to return
    1432           0 :       ctx->txn_out->err.custom_err = 0;
    1433           0 :       return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1434           0 :     }
    1435           0 :     ulong last_slot = deq_fd_landed_vote_t_peek_tail( new_state )->lockout.slot;
    1436           0 :     rc              = process_timestamp( vote_state, last_slot, timestamp, ctx );
    1437           0 :     if( FD_UNLIKELY( rc ) ) { return rc; }
    1438           0 :     vote_state->last_timestamp.timestamp = timestamp;
    1439           0 :   }
    1440             : 
    1441             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L754
    1442           0 :   vote_state->has_root_slot = (uchar)has_new_root;
    1443           0 :   vote_state->root_slot     = new_root;
    1444             : 
    1445             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L755
    1446           0 :   deq_fd_landed_vote_t_remove_all( vote_state->votes );
    1447           0 :   for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( new_state );
    1448           0 :        !deq_fd_landed_vote_t_iter_done( new_state, iter );
    1449           0 :        iter = deq_fd_landed_vote_t_iter_next( new_state, iter ) ) {
    1450           0 :     fd_landed_vote_t * landed_vote = deq_fd_landed_vote_t_iter_ele( new_state, iter );
    1451           0 :     deq_fd_landed_vote_t_push_tail_wrap( vote_state->votes, *landed_vote );
    1452           0 :   }
    1453             : 
    1454           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
    1455           0 : }
    1456             : 
    1457             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L849
    1458             : static int
    1459             : authorize( fd_borrowed_account_t *       vote_account,
    1460             :            fd_pubkey_t const *           authorized,
    1461             :            fd_vote_authorize_t           vote_authorize,
    1462             :            fd_pubkey_t const *           signers[static FD_TXN_SIG_MAX],
    1463             :            fd_sol_sysvar_clock_t const * clock,
    1464           0 :            fd_exec_instr_ctx_t const *   ctx /* feature_set */ ) {
    1465           0 :   int rc = 0;
    1466             : 
    1467             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L857
    1468             : 
    1469           0 :   rc = get_state( vote_account->acct, ctx->runtime->vote_program.authorize.vote_state_mem );
    1470           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1471           0 :   fd_vote_state_versioned_t * vote_state_versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.authorize.vote_state_mem;
    1472             : 
    1473           0 :   convert_to_current( vote_state_versioned,
    1474           0 :                       ctx->runtime->vote_program.authorize.authorized_voters_mem,
    1475           0 :                       ctx->runtime->vote_program.authorize.landed_votes_mem );
    1476           0 :   fd_vote_state_t * vote_state = &vote_state_versioned->inner.current;
    1477             : 
    1478             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L861
    1479           0 :   switch( vote_authorize.discriminant ) {
    1480             : 
    1481             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L862
    1482           0 :   case fd_vote_authorize_enum_voter:;
    1483             : 
    1484             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L863
    1485           0 :     int authorized_withdrawer_signer =
    1486           0 :         FD_EXECUTOR_INSTR_SUCCESS ==
    1487           0 :         verify_authorized_signer( &vote_state->authorized_withdrawer, signers );
    1488             : 
    1489             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L869-L872
    1490           0 :     ulong target_epoch;
    1491           0 :     rc = fd_ulong_checked_add( clock->leader_schedule_epoch, 1UL, &target_epoch );
    1492           0 :     if( FD_UNLIKELY( rc!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
    1493           0 :       return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
    1494           0 :     }
    1495             : 
    1496             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L866
    1497           0 :     rc = set_new_authorized_voter( vote_state,
    1498           0 :                                    authorized,
    1499           0 :                                    clock->epoch,
    1500           0 :                                    target_epoch,
    1501           0 :                                    authorized_withdrawer_signer,
    1502           0 :                                    signers,
    1503           0 :                                    ctx );
    1504           0 :     if( FD_UNLIKELY( rc ) ) return rc;
    1505           0 :     break;
    1506             : 
    1507             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L883
    1508           0 :   case fd_vote_authorize_enum_withdrawer:
    1509           0 :     rc = verify_authorized_signer( &vote_state->authorized_withdrawer, signers );
    1510           0 :     if( FD_UNLIKELY( rc ) ) return rc;
    1511           0 :     vote_state->authorized_withdrawer = *authorized;
    1512           0 :     break;
    1513             : 
    1514             :   // failing exhaustive check is fatal
    1515           0 :   default:
    1516           0 :     __builtin_unreachable();
    1517           0 :   }
    1518             : 
    1519             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L890
    1520           0 :   return set_vote_account_state( vote_account, vote_state, ctx, ctx->runtime->vote_program.authorize.vote_lockout_mem );
    1521           0 : }
    1522             : 
    1523             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L894
    1524             : static int
    1525             : update_validator_identity( fd_borrowed_account_t *     vote_account,
    1526             :                            fd_pubkey_t const *         node_pubkey,
    1527             :                            fd_pubkey_t const *         signers[static FD_TXN_SIG_MAX],
    1528           0 :                            fd_exec_instr_ctx_t const * ctx /* feature_set */ ) {
    1529           0 :   int rc = 0;
    1530             : 
    1531             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L900
    1532           0 :    rc = get_state( vote_account->acct, ctx->runtime->vote_program.update_validator_identity.vote_state_mem );
    1533           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1534           0 :   fd_vote_state_versioned_t * vote_state_versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.update_validator_identity.vote_state_mem;
    1535           0 :   convert_to_current( vote_state_versioned,
    1536           0 :                       ctx->runtime->vote_program.update_validator_identity.authorized_voters_mem,
    1537           0 :                       ctx->runtime->vote_program.update_validator_identity.landed_votes_mem );
    1538           0 :   fd_vote_state_t * vote_state = &vote_state_versioned->inner.current;
    1539             : 
    1540             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L905
    1541           0 :   rc = verify_authorized_signer( &vote_state->authorized_withdrawer, signers );
    1542           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1543             : 
    1544             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L908
    1545           0 :   rc = verify_authorized_signer( node_pubkey, signers );
    1546           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1547             : 
    1548             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L910
    1549           0 :   vote_state->node_pubkey = *node_pubkey;
    1550             : 
    1551             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L912
    1552           0 :   return set_vote_account_state( vote_account, vote_state, ctx, ctx->runtime->vote_program.update_validator_identity.vote_lockout_mem );
    1553           0 : }
    1554             : 
    1555             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L971
    1556             : static int
    1557           0 : is_commission_update_allowed( ulong slot, fd_epoch_schedule_t const * epoch_schedule ) {
    1558           0 :   if( FD_LIKELY( epoch_schedule->slots_per_epoch > 0UL ) ) {
    1559           0 :     ulong relative_slot = fd_ulong_sat_sub( slot, epoch_schedule->first_normal_slot );
    1560             :     // TODO underflow and overflow edge cases in addition to div by 0
    1561           0 :     relative_slot %= epoch_schedule->slots_per_epoch;
    1562           0 :     return fd_ulong_sat_mul( relative_slot, 2 ) <= epoch_schedule->slots_per_epoch;
    1563           0 :   } else {
    1564           0 :     return 1;
    1565           0 :   }
    1566           0 : }
    1567             : 
    1568             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L916
    1569             : static int
    1570             : update_commission( fd_borrowed_account_t *     vote_account,
    1571             :                    uchar                       commission,
    1572             :                    fd_pubkey_t const *         signers[static FD_TXN_SIG_MAX],
    1573             :                    fd_epoch_schedule_t const * epoch_schedule,
    1574             :                    fd_sol_sysvar_clock_t const * clock,
    1575           0 :                    fd_exec_instr_ctx_t const * ctx /* feature_set */ ) {
    1576           0 :   int rc = 0;
    1577             : 
    1578             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L925
    1579           0 :   fd_vote_state_versioned_t * vote_state_versioned = NULL;
    1580           0 :   fd_vote_state_t *           vote_state           = NULL;
    1581             : 
    1582             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L927
    1583           0 :   int enforce_commission_update_rule = 1;
    1584           0 :   rc = get_state( vote_account->acct, ctx->runtime->vote_program.update_commission.vote_state_mem );
    1585           0 :   if( FD_LIKELY( rc==FD_EXECUTOR_INSTR_SUCCESS ) ) {
    1586           0 :     vote_state_versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.update_commission.vote_state_mem;
    1587           0 :     convert_to_current( vote_state_versioned,
    1588           0 :                         ctx->runtime->vote_program.update_commission.authorized_voters_mem,
    1589           0 :                         ctx->runtime->vote_program.update_commission.landed_votes_mem );
    1590           0 :     vote_state = &vote_state_versioned->inner.current;
    1591           0 :     enforce_commission_update_rule = commission > vote_state->commission;
    1592           0 :   }
    1593             : 
    1594             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L940
    1595           0 :   if( FD_LIKELY( enforce_commission_update_rule ) ) {
    1596           0 :     if( FD_UNLIKELY( !is_commission_update_allowed( clock->slot, epoch_schedule ) ) ) {
    1597           0 :       ctx->txn_out->err.custom_err = FD_VOTE_ERR_COMMISSION_UPDATE_TOO_LATE;
    1598           0 :       return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1599           0 :     }
    1600           0 :   }
    1601             : 
    1602             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L949
    1603           0 :   if( !vote_state ) {
    1604           0 :     rc = get_state( vote_account->acct, ctx->runtime->vote_program.update_commission.vote_state_mem );
    1605           0 :     if( FD_UNLIKELY( rc ) ) return rc;
    1606           0 :     vote_state_versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.update_commission.vote_state_mem;
    1607           0 :     convert_to_current( vote_state_versioned,
    1608           0 :                         ctx->runtime->vote_program.update_commission.authorized_voters_mem,
    1609           0 :                         ctx->runtime->vote_program.update_commission.landed_votes_mem );
    1610           0 :     vote_state = &vote_state_versioned->inner.current;
    1611           0 :   }
    1612             : 
    1613             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L957
    1614           0 :   rc = verify_authorized_signer( &vote_state->authorized_withdrawer, signers );
    1615           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1616             : 
    1617             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L959
    1618           0 :   vote_state->commission = commission;
    1619             : 
    1620             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L961
    1621           0 :   return set_vote_account_state( vote_account, vote_state, ctx, ctx->runtime->vote_program.update_commission.vote_lockout_mem );
    1622           0 : }
    1623             : 
    1624             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L997
    1625             : static int
    1626             : withdraw( fd_exec_instr_ctx_t const *   ctx,
    1627             :           fd_borrowed_account_t *       vote_account,
    1628             :           ulong                         lamports,
    1629             :           ushort                        to_account_index,
    1630             :           fd_pubkey_t const *           signers[static FD_TXN_SIG_MAX],
    1631             :           fd_rent_t const *             rent_sysvar,
    1632           0 :           fd_sol_sysvar_clock_t const * clock ) {
    1633           0 :   int rc = 0;
    1634             : 
    1635             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1010
    1636           0 :   rc = get_state( vote_account->acct, ctx->runtime->vote_program.withdraw.vote_state_mem );
    1637           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1638           0 :   fd_vote_state_versioned_t * vote_state_versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.withdraw.vote_state_mem;
    1639             : 
    1640           0 :   convert_to_current( vote_state_versioned,
    1641           0 :                       ctx->runtime->vote_program.withdraw.authorized_voters_mem,
    1642           0 :                       ctx->runtime->vote_program.withdraw.landed_votes_mem );
    1643           0 :   fd_vote_state_t * vote_state = &vote_state_versioned->inner.current;
    1644             : 
    1645             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1014
    1646           0 :   rc = verify_authorized_signer( &vote_state->authorized_withdrawer, signers );
    1647           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1648             : 
    1649             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1016
    1650           0 :   if( FD_UNLIKELY( lamports > fd_borrowed_account_get_lamports( vote_account ) ) )
    1651           0 :     return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
    1652           0 :   ulong remaining_balance = fd_borrowed_account_get_lamports( vote_account ) - lamports;
    1653             : 
    1654             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1021
    1655           0 :   if( FD_UNLIKELY( remaining_balance == 0 ) ) {
    1656             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1014
    1657           0 :     int reject_active_vote_account_close = 0;
    1658             : 
    1659           0 :     ulong last_epoch_with_credits;
    1660           0 :     if( FD_LIKELY( !deq_fd_vote_epoch_credits_t_empty( vote_state->epoch_credits ) ) ) {
    1661           0 :       last_epoch_with_credits =
    1662           0 :           deq_fd_vote_epoch_credits_t_peek_tail_const( vote_state->epoch_credits )->epoch;
    1663           0 :       ulong current_epoch = clock->epoch;
    1664           0 :       reject_active_vote_account_close =
    1665           0 :           fd_ulong_sat_sub( current_epoch, last_epoch_with_credits ) < 2;
    1666           0 :     }
    1667             : 
    1668             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1034
    1669           0 :     if( FD_UNLIKELY( reject_active_vote_account_close ) ) {
    1670             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1036
    1671           0 :       ctx->txn_out->err.custom_err = FD_VOTE_ERR_ACTIVE_VOTE_ACCOUNT_CLOSE;
    1672           0 :       return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1673           0 :     } else {
    1674             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1040
    1675           0 :       fd_vote_state_versioned_t vote_state_versions;
    1676           0 :       fd_vote_state_versioned_new_disc( &vote_state_versions,
    1677           0 :                                         fd_vote_state_versioned_enum_current );
    1678           0 :       vote_state_versions.inner.current.prior_voters.idx      = 31;
    1679           0 :       vote_state_versions.inner.current.prior_voters.is_empty = 1;
    1680           0 :       fd_vote_state_t * default_vote_state                    = &vote_state_versions.inner.current;
    1681           0 :       rc                                                      = 0;
    1682           0 :       rc = set_vote_account_state( vote_account, default_vote_state, ctx, ctx->runtime->vote_program.withdraw.vote_lockout_mem );
    1683           0 :       if( FD_UNLIKELY( rc != 0 ) ) return rc;
    1684           0 :     }
    1685           0 :   } else {
    1686             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1043
    1687           0 :     ulong min_rent_exempt_balance =
    1688           0 :         fd_rent_exempt_minimum_balance( rent_sysvar, fd_borrowed_account_get_data_len( vote_account ) );
    1689           0 :     if( remaining_balance < min_rent_exempt_balance ) {
    1690           0 :       return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
    1691           0 :     }
    1692           0 :   }
    1693             : 
    1694             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1049
    1695           0 :   rc = fd_borrowed_account_checked_sub_lamports( vote_account, lamports );
    1696           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1697             : 
    1698             :   /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/vote/src/vote_state/mod.rs#L1019 */
    1699           0 :   fd_borrowed_account_drop( vote_account );
    1700             : 
    1701             :   /* https://github.com/anza-xyz/agave/blob/v2.1.14/programs/vote/src/vote_state/mod.rs#L1020-L1021 */
    1702           0 :   fd_guarded_borrowed_account_t to = {0};
    1703           0 :   FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, to_account_index, &to );
    1704             : 
    1705             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1053
    1706           0 :   rc = fd_borrowed_account_checked_add_lamports( &to, lamports );
    1707           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1708             : 
    1709           0 :   return 0;
    1710           0 : }
    1711             : 
    1712             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L760
    1713             : static int
    1714             : process_vote_unfiltered( fd_vote_state_t *           vote_state,
    1715             :                          ulong *                     vote_slots,
    1716             :                          fd_vote_t const *           vote,
    1717             :                          fd_slot_hash_t const *      slot_hashes, /* deque */
    1718             :                          ulong                       epoch,
    1719             :                          ulong                       current_slot,
    1720           0 :                          fd_exec_instr_ctx_t const * ctx ) {
    1721           0 :   int rc;
    1722             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L770
    1723           0 :   rc = check_slots_are_valid( vote_state, vote_slots, &vote->hash, slot_hashes, ctx );
    1724           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1725           0 :   for( deq_ulong_iter_t iter = deq_ulong_iter_init( vote_slots );
    1726           0 :        !deq_ulong_iter_done( vote_slots, iter );
    1727           0 :        iter = deq_ulong_iter_next( vote_slots, iter ) ) {
    1728           0 :     ulong * ele = deq_ulong_iter_ele( vote_slots, iter );
    1729             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L772
    1730           0 :     process_next_vote_slot( vote_state, *ele, epoch, current_slot );
    1731           0 :   }
    1732           0 :   return 0;
    1733           0 : }
    1734             : 
    1735             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L783
    1736             : static int
    1737             : process_vote( fd_vote_state_t *           vote_state,
    1738             :               fd_vote_t const *           vote,
    1739             :               fd_slot_hash_t const *      slot_hashes, /* deque */
    1740             :               ulong                       epoch,
    1741             :               ulong                       current_slot,
    1742           0 :               fd_exec_instr_ctx_t const * ctx ) {
    1743             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L792
    1744           0 :   if( FD_UNLIKELY( deq_ulong_empty( vote->slots ) ) ) {
    1745           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_EMPTY_SLOTS;
    1746           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1747           0 :   }
    1748             : 
    1749             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L795
    1750           0 :   ulong earliest_slot_in_history = 0;
    1751           0 :   if( FD_UNLIKELY( !deq_fd_slot_hash_t_empty( slot_hashes ) ) ) {
    1752           0 :     earliest_slot_in_history = deq_fd_slot_hash_t_peek_tail_const( slot_hashes )->slot;
    1753           0 :   }
    1754             : 
    1755             :   /* We know that the size of the vote_slots is bounded by the number of
    1756             :      slots that can fit inside of an instruction.  A very loose bound is
    1757             :      assuming that the entire transaction is just filled with a vote
    1758             :      slot deque (1232 bytes per transaction/8 bytes per slot) == 154
    1759             :      slots.  The footprint of a deque is as follows:
    1760             :      fd_ulong_align_up( fd_ulong_align_up( 32UL, alignof(DEQUE_T) ) + sizeof(DEQUE_T)*max, alignof(DEQUE_(private_t)) );
    1761             :      So, the footprint in our case is:
    1762             :      fd_ulong_align_up( fd_ulong_align_up( 32UL, alignof(ulong) ) + sizeof(ulong)*154, alignof(DEQUE_(private_t)) );
    1763             :      Which is equal to
    1764             :      fd_ulong_align_up( 32UL + 154 * 8UL, 8UL ) = 1264UL; */
    1765           0 :   #define VOTE_SLOTS_MAX             (FD_TXN_MTU/sizeof(ulong))
    1766           0 :   #define VOTE_SLOTS_DEQUE_FOOTPRINT (1264UL )
    1767           0 :   #define VOTE_SLOTS_DEQUE_ALIGN     (8UL)
    1768           0 :   FD_TEST( deq_ulong_footprint( VOTE_SLOTS_MAX ) == VOTE_SLOTS_DEQUE_FOOTPRINT );
    1769           0 :   FD_TEST( deq_ulong_align()                     == 8UL );
    1770           0 :   FD_TEST( deq_ulong_cnt( vote->slots )          <= VOTE_SLOTS_MAX );
    1771           0 :   uchar * vote_slots_mem[ VOTE_SLOTS_DEQUE_FOOTPRINT ] __attribute__((aligned(VOTE_SLOTS_DEQUE_ALIGN)));
    1772             : 
    1773             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L796
    1774           0 :   ulong * vote_slots = deq_ulong_join( deq_ulong_new( vote_slots_mem, deq_ulong_cnt( vote->slots ) ) );
    1775           0 :   for( deq_ulong_iter_t iter = deq_ulong_iter_init( vote->slots );
    1776           0 :        !deq_ulong_iter_done( vote->slots, iter );
    1777           0 :        iter = deq_ulong_iter_next( vote->slots, iter ) ) {
    1778           0 :     ulong * ele = deq_ulong_iter_ele( vote->slots, iter );
    1779           0 :     if( FD_UNLIKELY( *ele >= earliest_slot_in_history ) ) {
    1780           0 :       vote_slots = deq_ulong_push_tail_wrap( vote_slots, *ele );
    1781           0 :     }
    1782           0 :   }
    1783             : 
    1784             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L802
    1785           0 :   if( FD_UNLIKELY( deq_ulong_cnt( vote_slots ) == 0 ) ) {
    1786           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_VOTES_TOO_OLD_ALL_FILTERED;
    1787           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1788           0 :   }
    1789             : 
    1790             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L805
    1791           0 :   return process_vote_unfiltered(
    1792           0 :       vote_state, vote_slots, vote, slot_hashes, epoch, current_slot, ctx );
    1793           0 : }
    1794             : 
    1795             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1060
    1796             : static int
    1797             : initialize_account( fd_borrowed_account_t *       vote_account,
    1798             :                     fd_vote_init_t *              vote_init,
    1799             :                     fd_pubkey_t const *           signers[static FD_TXN_SIG_MAX],
    1800             :                     fd_sol_sysvar_clock_t const * clock,
    1801           0 :                     fd_exec_instr_ctx_t const *   ctx /* feature_set */ ) {
    1802           0 :   int rc;
    1803             : 
    1804             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1067
    1805           0 :   ulong data_len = fd_borrowed_account_get_data_len( vote_account );
    1806           0 :   if( FD_UNLIKELY( data_len != size_of_versioned( 1 ) ) ) {
    1807           0 :     return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
    1808           0 :   }
    1809             : 
    1810             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1074
    1811           0 :   rc = get_state( vote_account->acct, ctx->runtime->vote_program.init_account.vote_state_mem );
    1812           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1813           0 :   fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.init_account.vote_state_mem;
    1814             : 
    1815             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1076
    1816           0 :   if( FD_UNLIKELY( !is_uninitialized( versioned ) ) ) {
    1817           0 :     return FD_EXECUTOR_INSTR_ERR_ACC_ALREADY_INITIALIZED;
    1818           0 :   }
    1819             : 
    1820             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1081
    1821           0 :   rc = verify_authorized_signer( &vote_init->node_pubkey, signers );
    1822           0 :   if( FD_UNLIKELY( rc ) ) {
    1823           0 :     return rc;
    1824           0 :   }
    1825             : 
    1826             :   /*
    1827             :    * N.B. Technically we should destroy() to release memory before
    1828             :    * newing, otherwise the pointers are wiped and memory is leaked.
    1829             :    * We are probably fine for now since we are bump allocating
    1830             :    * everything and the enclosing frame will free everything when
    1831             :    * popped.
    1832             :    */
    1833             :   // reset the object
    1834           0 :   fd_vote_state_versioned_new( versioned );
    1835             : 
    1836             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1083
    1837           0 :   vote_state_new( vote_init,
    1838           0 :                   clock,
    1839           0 :                   ctx->runtime->vote_program.init_account.authorized_voters_mem,
    1840           0 :                   &versioned->inner.current );
    1841           0 :   return set_vote_account_state( vote_account, &versioned->inner.current, ctx, ctx->runtime->vote_program.init_account.vote_lockout_mem );
    1842           0 : }
    1843             : 
    1844             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1086
    1845             : static int
    1846             : verify_and_get_vote_state( fd_borrowed_account_t *       vote_account,
    1847             :                            fd_sol_sysvar_clock_t const * clock,
    1848             :                            fd_pubkey_t const *           signers[FD_TXN_SIG_MAX],
    1849             :                            fd_vote_state_t *             vote_state /* out */,
    1850             :                            uchar *                       vote_state_mem,
    1851             :                            uchar *                       authorized_voters_mem,
    1852           0 :                            uchar *                       landed_votes_mem ) {
    1853           0 :   int rc = 0;
    1854             : 
    1855             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1091
    1856           0 :   rc = get_state( vote_account->acct, vote_state_mem );
    1857           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1858           0 :   fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)vote_state_mem;
    1859             : 
    1860             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1093
    1861           0 :   if( FD_UNLIKELY( is_uninitialized( versioned ) ) )
    1862           0 :     return FD_EXECUTOR_INSTR_ERR_UNINITIALIZED_ACCOUNT;
    1863             : 
    1864             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1097
    1865           0 :   convert_to_current( versioned, authorized_voters_mem, landed_votes_mem );
    1866           0 :   *vote_state = versioned->inner.current;
    1867             : 
    1868             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1098
    1869           0 :   fd_pubkey_t * authorized_voter = NULL;
    1870           0 :   rc = get_and_update_authorized_voter( vote_state, clock->epoch, &authorized_voter );
    1871           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1872             : 
    1873             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1099
    1874           0 :   rc = verify_authorized_signer( authorized_voter, signers );
    1875           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1876             : 
    1877           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
    1878           0 : }
    1879             : 
    1880             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1104
    1881             : static int
    1882             : process_vote_with_account( fd_borrowed_account_t *       vote_account,
    1883             :                            fd_slot_hash_t const *        slot_hashes, /* deque */
    1884             :                            fd_sol_sysvar_clock_t const * clock,
    1885             :                            fd_vote_t *                   vote,
    1886             :                            fd_pubkey_t const *           signers[static FD_TXN_SIG_MAX],
    1887           0 :                            fd_exec_instr_ctx_t const *   ctx ) {
    1888             : 
    1889           0 :   int             rc;
    1890           0 :   fd_vote_state_t vote_state;
    1891             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1112
    1892           0 :   rc = verify_and_get_vote_state( vote_account,
    1893           0 :                                   clock,
    1894           0 :                                   signers,
    1895           0 :                                   &vote_state,
    1896           0 :                                   ctx->runtime->vote_program.process_vote.vote_state_mem,
    1897           0 :                                   ctx->runtime->vote_program.process_vote.authorized_voters_mem,
    1898           0 :                                   ctx->runtime->vote_program.process_vote.landed_votes_mem );
    1899           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1900             : 
    1901             : 
    1902             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1117
    1903           0 :   rc = process_vote( &vote_state, vote, slot_hashes, clock->epoch, clock->slot, ctx );
    1904           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1905             : 
    1906             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1126
    1907           0 :   if( FD_LIKELY( vote->has_timestamp ) ) {
    1908           0 :     if( FD_UNLIKELY( deq_ulong_cnt( vote->slots ) == 0 ) ) {
    1909           0 :       ctx->txn_out->err.custom_err = FD_VOTE_ERR_EMPTY_SLOTS;
    1910           0 :       return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1911           0 :     }
    1912             : 
    1913           0 :     ulong max = deq_ulong_peek_head( vote->slots ) ? *deq_ulong_peek_head( vote->slots ) : 0UL;
    1914             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1127
    1915           0 :     for( deq_ulong_iter_t iter = deq_ulong_iter_init( vote->slots );
    1916           0 :          !deq_ulong_iter_done( vote->slots, iter );
    1917           0 :          iter = deq_ulong_iter_next( vote->slots, iter ) ) {
    1918           0 :       ulong * ele = deq_ulong_iter_ele( vote->slots, iter );
    1919           0 :       max         = fd_ulong_max( max, *ele );
    1920           0 :     }
    1921           0 :     if( FD_UNLIKELY( !max ) ) {
    1922           0 :       ctx->txn_out->err.custom_err = FD_VOTE_ERR_EMPTY_SLOTS;
    1923           0 :       return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1924           0 :     }
    1925             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1131
    1926           0 :     rc = process_timestamp( &vote_state, max, vote->timestamp, ctx );
    1927           0 :     if( FD_UNLIKELY( rc ) ) return rc;
    1928           0 :   }
    1929             : 
    1930             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1133
    1931           0 :   return set_vote_account_state( vote_account, &vote_state, ctx, ctx->runtime->vote_program.process_vote.vote_lockout_mem );
    1932           0 : }
    1933             : 
    1934             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1156
    1935             : static int
    1936             : do_process_vote_state_update( fd_vote_state_t *           vote_state,
    1937             :                               fd_slot_hash_t const *      slot_hashes, /* deque */
    1938             :                               ulong                       epoch,
    1939             :                               ulong                       slot,
    1940             :                               fd_vote_state_update_t *    vote_state_update,
    1941           0 :                               fd_exec_instr_ctx_t const * ctx /* feature_set */ ) {
    1942           0 :   int rc;
    1943             : 
    1944             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1164
    1945           0 :   rc = check_and_filter_proposed_vote_state(
    1946           0 :       vote_state,
    1947           0 :       vote_state_update->lockouts, &vote_state_update->has_root, &vote_state_update->root, &vote_state_update->hash,
    1948           0 :       slot_hashes, ctx );
    1949           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1950             : 
    1951             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1177
    1952           0 :   fd_landed_vote_t * landed_votes = deq_fd_landed_vote_t_join( deq_fd_landed_vote_t_new( ctx->runtime->vote_program.process_vote.vs_update_landed_votes_mem, deq_fd_vote_lockout_t_cnt( vote_state_update->lockouts ) ) );
    1953           0 :   for( deq_fd_vote_lockout_t_iter_t iter =
    1954           0 :            deq_fd_vote_lockout_t_iter_init( vote_state_update->lockouts );
    1955           0 :        !deq_fd_vote_lockout_t_iter_done( vote_state_update->lockouts, iter );
    1956           0 :        iter = deq_fd_vote_lockout_t_iter_next( vote_state_update->lockouts, iter ) ) {
    1957           0 :     fd_vote_lockout_t * lockout =
    1958           0 :         deq_fd_vote_lockout_t_iter_ele( vote_state_update->lockouts, iter );
    1959           0 :     deq_fd_landed_vote_t_push_tail_wrap( landed_votes,
    1960           0 :                                     ( fd_landed_vote_t ){ .latency = 0, .lockout = *lockout } );
    1961           0 :   }
    1962             : 
    1963             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1171
    1964           0 :   return process_new_vote_state( vote_state,
    1965           0 :                                  landed_votes,
    1966           0 :                                  vote_state_update->has_root,
    1967           0 :                                  vote_state_update->root,
    1968           0 :                                  vote_state_update->has_timestamp,
    1969           0 :                                  vote_state_update->timestamp,
    1970           0 :                                  epoch,
    1971           0 :                                  slot,
    1972           0 :                                  ctx );
    1973           0 : }
    1974             : 
    1975             : static int
    1976             : process_vote_state_update( fd_borrowed_account_t *       vote_account,
    1977             :                            fd_slot_hash_t const *        slot_hashes,
    1978             :                            fd_sol_sysvar_clock_t const * clock,
    1979             :                            fd_vote_state_update_t *      vote_state_update,
    1980             :                            fd_pubkey_t const *           signers[static FD_TXN_SIG_MAX],
    1981           0 :                            fd_exec_instr_ctx_t const *   ctx /* feature_set */ ) {
    1982           0 :   fd_vote_state_t vote_state;
    1983             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1144
    1984           0 :   int rc = verify_and_get_vote_state( vote_account,
    1985           0 :                                       clock,
    1986           0 :                                       signers,
    1987           0 :                                       &vote_state,
    1988           0 :                                       ctx->runtime->vote_program.process_vote.vote_state_mem,
    1989           0 :                                       ctx->runtime->vote_program.process_vote.authorized_voters_mem,
    1990           0 :                                       ctx->runtime->vote_program.process_vote.landed_votes_mem );
    1991           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1992             : 
    1993             : 
    1994             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1145
    1995           0 :   rc = do_process_vote_state_update(
    1996           0 :       &vote_state, slot_hashes, clock->epoch, clock->slot, vote_state_update, ctx );
    1997           0 :   if( FD_UNLIKELY( rc ) ) {
    1998           0 :     return rc;
    1999           0 :   }
    2000             : 
    2001             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1153
    2002           0 :   rc = set_vote_account_state( vote_account, &vote_state, ctx, ctx->runtime->vote_program.process_vote.vote_lockout_mem );
    2003             : 
    2004           0 :   return rc;
    2005           0 : }
    2006             : 
    2007             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1206
    2008             : static int
    2009             : do_process_tower_sync( fd_vote_state_t *           vote_state,
    2010             :                        fd_slot_hash_t const *      slot_hashes, /* deque */
    2011             :                        ulong                       epoch,
    2012             :                        ulong                       slot,
    2013             :                        fd_tower_sync_t *           tower_sync,
    2014           0 :                        fd_exec_instr_ctx_t const * ctx /* feature_set */ ) {
    2015             : 
    2016           0 :   do {
    2017             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1214
    2018           0 :     int err = check_and_filter_proposed_vote_state(
    2019           0 :         vote_state,
    2020           0 :         tower_sync->lockouts, &tower_sync->has_root, &tower_sync->root, &tower_sync->hash,
    2021           0 :         slot_hashes, ctx );
    2022           0 :     if( FD_UNLIKELY( err ) ) return err;
    2023           0 :   } while(0);
    2024             : 
    2025             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1221
    2026           0 :   return process_new_vote_state(
    2027           0 :       vote_state,
    2028           0 :       landed_votes_from_lockouts( tower_sync->lockouts, ctx->runtime->vote_program.tower_sync.tower_sync_landed_votes_mem ),
    2029           0 :       tower_sync->has_root,
    2030           0 :       tower_sync->root,
    2031           0 :       tower_sync->has_timestamp,
    2032           0 :       tower_sync->timestamp,
    2033           0 :       epoch,
    2034           0 :       slot,
    2035           0 :       ctx );
    2036           0 : }
    2037             : 
    2038             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1186
    2039             : static int
    2040             : process_tower_sync( fd_borrowed_account_t *       vote_account,
    2041             :                     fd_slot_hash_t const *        slot_hashes, /* deque */
    2042             :                     fd_sol_sysvar_clock_t const * clock,
    2043             :                     fd_tower_sync_t *             tower_sync,
    2044             :                     fd_pubkey_t const *           signers[static FD_TXN_SIG_MAX],
    2045           0 :                     fd_exec_instr_ctx_t const *   ctx /* feature_set */ ) {
    2046             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1194
    2047           0 :   fd_vote_state_t vote_state;
    2048           0 :   do {
    2049           0 :     int err = verify_and_get_vote_state( vote_account,
    2050           0 :                                          clock,
    2051           0 :                                          signers,
    2052           0 :                                          &vote_state,
    2053           0 :                                          ctx->runtime->vote_program.tower_sync.vote_state_mem,
    2054           0 :                                          ctx->runtime->vote_program.tower_sync.authorized_voters_mem,
    2055           0 :                                          ctx->runtime->vote_program.tower_sync.vote_state_landed_votes_mem );
    2056           0 :     if( FD_UNLIKELY( err ) ) return err;
    2057           0 :   } while(0);
    2058             : 
    2059             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1195
    2060           0 :   do {
    2061           0 :     int err = do_process_tower_sync( &vote_state, slot_hashes, clock->epoch, clock->slot, tower_sync, ctx );
    2062           0 :     if( FD_UNLIKELY( err ) ) return err;
    2063           0 :   } while(0);
    2064             : 
    2065             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1203
    2066           0 :   return set_vote_account_state( vote_account, &vote_state, ctx, ctx->runtime->vote_program.process_vote.vote_lockout_mem );
    2067           0 : }
    2068             : 
    2069             : /**********************************************************************/
    2070             : /* FD-only encoders / decoders (doesn't map directly to Labs impl)    */
    2071             : /**********************************************************************/
    2072             : 
    2073             : int
    2074             : fd_vote_decode_compact_update( fd_compact_vote_state_update_t * compact_update,
    2075             :                                fd_vote_state_update_t *         vote_update,
    2076           0 :                                fd_exec_instr_ctx_t const *      ctx ) {
    2077             :   // Taken from:
    2078             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L954
    2079           0 :   if( compact_update->root != ULONG_MAX ) {
    2080           0 :     vote_update->has_root = 1;
    2081           0 :     vote_update->root     = compact_update->root;
    2082           0 :   } else {
    2083           0 :     vote_update->has_root = 0;
    2084           0 :     vote_update->root     = ULONG_MAX;
    2085           0 :   }
    2086             : 
    2087           0 :   ulong lockouts_len = compact_update->lockouts_len;
    2088           0 :   ulong lockouts_max = fd_ulong_max( lockouts_len, MAX_LOCKOUT_HISTORY );
    2089             : 
    2090           0 :   vote_update->lockouts = deq_fd_vote_lockout_t_join( deq_fd_vote_lockout_t_new( ctx->runtime->vote_program.process_vote.compact_vs_lockout_mem, lockouts_max ) );
    2091           0 :   ulong slot            = fd_ulong_if( vote_update->has_root, vote_update->root, 0 );
    2092             : 
    2093           0 :   for( ulong i=0; i < lockouts_len; ++i ) {
    2094           0 :     fd_vote_lockout_t * elem = deq_fd_vote_lockout_t_push_tail_nocopy( vote_update->lockouts );
    2095           0 :     fd_vote_lockout_new( elem );
    2096             : 
    2097           0 :     fd_lockout_offset_t * lock_offset = &compact_update->lockouts[i];
    2098             : 
    2099           0 :     ulong next_slot;
    2100           0 :     if( FD_UNLIKELY( __builtin_uaddl_overflow( slot, lock_offset->offset, &next_slot ) ) )
    2101           0 :       return 0;
    2102             : 
    2103           0 :     elem->slot = slot        = next_slot;
    2104           0 :     elem->confirmation_count = (uint)lock_offset->confirmation_count;
    2105           0 :   }
    2106             : 
    2107           0 :   vote_update->hash          = compact_update->hash;
    2108           0 :   vote_update->has_timestamp = compact_update->has_timestamp;
    2109           0 :   vote_update->timestamp     = compact_update->timestamp;
    2110             : 
    2111           0 :   return 1;
    2112           0 : }
    2113             : 
    2114             : /**********************************************************************/
    2115             : /* mod vote_processor                                                 */
    2116             : /**********************************************************************/
    2117             : 
    2118             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L21
    2119             : static int
    2120             : process_authorize_with_seed_instruction(
    2121             :     /* invoke_context */
    2122             :     fd_exec_instr_ctx_t const * ctx,
    2123             :     /* transaction_context */
    2124             :     fd_borrowed_account_t * vote_account,
    2125             :     fd_pubkey_t const *     new_authority,
    2126             :     fd_vote_authorize_t     authorization_type,
    2127             :     fd_pubkey_t const *     current_authority_derived_key_owner,
    2128             :     uchar const *           current_authority_derived_key_seed,
    2129           0 :     ulong                   current_authority_derived_key_seed_len ) {
    2130           0 :   int rc = 0;
    2131             : 
    2132             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L31
    2133           0 :   rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_clock_id );
    2134           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    2135             : 
    2136           0 :   fd_sol_sysvar_clock_t clock_;
    2137           0 :   fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
    2138           0 :   if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    2139             : 
    2140           0 :   fd_pubkey_t * expected_authority_keys[FD_TXN_SIG_MAX] = { 0 };
    2141           0 :   fd_pubkey_t   single_signer                        = { 0 };
    2142             : 
    2143           0 :   if( ctx->instr->acct_cnt < 3 )
    2144           0 :     return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
    2145             : 
    2146             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L33
    2147           0 :   if( fd_instr_acc_is_signer_idx( ctx->instr, 2, &rc ) ) {
    2148             : 
    2149             :     // https://github.com/anza-xyz/agave/blob/v2.1.14/programs/vote/src/vote_processor.rs#L34
    2150           0 :     fd_pubkey_t const * base_pubkey = NULL;
    2151           0 :     rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 2UL, &base_pubkey );
    2152           0 :     if( FD_UNLIKELY( rc ) ) return rc;
    2153             : 
    2154             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L37
    2155           0 :     expected_authority_keys[0] = &single_signer;
    2156           0 :     rc = fd_pubkey_create_with_seed( ctx,
    2157           0 :                                      base_pubkey->uc,
    2158           0 :                                      (char const *)current_authority_derived_key_seed,
    2159           0 :                                      current_authority_derived_key_seed_len,
    2160           0 :                                      current_authority_derived_key_owner->uc,
    2161           0 :                                      /* insert */ expected_authority_keys[0]->uc );
    2162           0 :     if( FD_UNLIKELY( rc ) ) return rc;
    2163           0 :   }
    2164             : 
    2165             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L43
    2166           0 :   return authorize( vote_account,
    2167           0 :                     new_authority,
    2168           0 :                     authorization_type,
    2169           0 :                     (fd_pubkey_t const **)expected_authority_keys,
    2170           0 :                     clock,
    2171           0 :                     ctx );
    2172           0 : }
    2173             : 
    2174             : /**********************************************************************/
    2175             : /* Entry point for the Vote Program                                   */
    2176             : /**********************************************************************/
    2177             : 
    2178             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L57
    2179             : int
    2180           0 : fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ) {
    2181             :   /* FD-specific init */
    2182           0 :   int rc = FD_EXECUTOR_INSTR_SUCCESS;
    2183             : 
    2184             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L57
    2185           0 :   FD_EXEC_CU_UPDATE( ctx, DEFAULT_COMPUTE_UNITS );
    2186             : 
    2187             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L64
    2188           0 :   if( FD_UNLIKELY( ctx->instr->acct_cnt < 1 ) ) {
    2189           0 :     return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
    2190           0 :   }
    2191             : 
    2192           0 :   fd_guarded_borrowed_account_t me = {0};
    2193           0 :   FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, 0, &me );
    2194             : 
    2195           0 :   switch( rc ) {
    2196           0 :   case FD_ACC_MGR_SUCCESS:
    2197           0 :     break;
    2198           0 :   case FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT:
    2199             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/src/transaction_context.rs#L637
    2200           0 :     return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
    2201           0 :   default:
    2202             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/src/transaction_context.rs#L639
    2203           0 :     return FD_EXECUTOR_INSTR_ERR_ACC_BORROW_FAILED;
    2204           0 :   }
    2205             : 
    2206             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L65
    2207           0 :   if( FD_UNLIKELY( 0 != memcmp( fd_borrowed_account_get_owner( &me ),
    2208           0 :                                 fd_solana_vote_program_id.key,
    2209           0 :                                 sizeof( fd_pubkey_t ) ) ) ) {
    2210             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L66
    2211           0 :     return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
    2212           0 :   }
    2213             : 
    2214             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L69
    2215           0 :   fd_pubkey_t const * signers[FD_TXN_SIG_MAX] = { 0 };
    2216           0 :   fd_exec_instr_ctx_get_signers( ctx, signers );
    2217             : 
    2218           0 :   uchar __attribute__((aligned(alignof(fd_vote_instruction_t)))) vote_instruction_mem[ FD_VOTE_INSTRUCTION_FOOTPRINT ];
    2219           0 :   fd_vote_instruction_t * instruction = fd_bincode_decode_static_limited_deserialize(
    2220           0 :       vote_instruction,
    2221           0 :       vote_instruction_mem,
    2222           0 :       ctx->instr->data,
    2223           0 :       ctx->instr->data_sz,
    2224           0 :       FD_TXN_MTU,
    2225           0 :       NULL );
    2226           0 :   if( FD_UNLIKELY( !instruction ) ) {
    2227           0 :     return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
    2228           0 :   }
    2229             : 
    2230             :   /* PLEASE PRESERVE SWITCH-CASE ORDERING TO MIRROR LABS IMPL:
    2231             :    */
    2232           0 :   switch( instruction->discriminant ) {
    2233             : 
    2234             :   /* InitializeAccount
    2235             :    *
    2236             :    * Instruction:
    2237             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L32
    2238             :    *
    2239             :    * Processor:
    2240             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L71
    2241             :    */
    2242           0 :   case fd_vote_instruction_enum_initialize_account: {
    2243             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L72
    2244           0 :     rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_rent_id );
    2245           0 :     if( FD_UNLIKELY( rc ) ) return rc;
    2246           0 :     fd_rent_t rent_;
    2247           0 :     fd_rent_t const * rent = fd_sysvar_cache_rent_read( ctx->sysvar_cache, &rent_ );
    2248           0 :     if( FD_UNLIKELY( !rent ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    2249             : 
    2250           0 :     if( FD_UNLIKELY( fd_borrowed_account_get_lamports( &me ) <
    2251           0 :                      fd_rent_exempt_minimum_balance( rent, fd_borrowed_account_get_data_len( &me ) ) ) )
    2252           0 :       return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
    2253             : 
    2254             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L76
    2255           0 :     rc = fd_sysvar_instr_acct_check( ctx, 2, &fd_sysvar_clock_id );
    2256           0 :     if( FD_UNLIKELY( rc ) ) return rc;
    2257           0 :     fd_sol_sysvar_clock_t clock_;
    2258           0 :     fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
    2259           0 :     if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    2260             : 
    2261             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L78
    2262           0 :     rc = initialize_account( &me,
    2263           0 :                              &instruction->inner.initialize_account,
    2264           0 :                              signers,
    2265           0 :                              clock,
    2266           0 :                              ctx );
    2267             : 
    2268           0 :     break;
    2269           0 :   }
    2270             : 
    2271             :   /* Authorize
    2272             :    *
    2273             :    * Instruction:
    2274             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L40
    2275             :    *
    2276             :    * Processor:
    2277             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L86
    2278             :    *
    2279             :    * Notes:
    2280             :    * - Up to two signers: the vote authority and the authorized withdrawer.
    2281             :    */
    2282           0 :   case fd_vote_instruction_enum_authorize: {
    2283             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L87
    2284           0 :     rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_clock_id );
    2285           0 :     if( FD_UNLIKELY( rc ) ) return rc;
    2286           0 :     fd_sol_sysvar_clock_t clock_;
    2287           0 :     fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
    2288           0 :     if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    2289             : 
    2290             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L89
    2291           0 :     fd_pubkey_t const * voter_pubkey   = &instruction->inner.authorize.pubkey;
    2292           0 :     fd_vote_authorize_t vote_authorize = instruction->inner.authorize.vote_authorize;
    2293             : 
    2294           0 :     rc = authorize( &me, voter_pubkey, vote_authorize, signers, clock, ctx );
    2295             : 
    2296           0 :     break;
    2297           0 :   }
    2298             : 
    2299             :   /* AuthorizeWithSeed
    2300             :    *
    2301             :    * Instruction:
    2302             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L117
    2303             :    *
    2304             :    * Processor:
    2305             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L98
    2306             :    */
    2307           0 :   case fd_vote_instruction_enum_authorize_with_seed: {
    2308             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L99
    2309           0 :     if( FD_UNLIKELY( ctx->instr->acct_cnt < 3 ) ) {
    2310           0 :       rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
    2311           0 :       break;
    2312           0 :     }
    2313             : 
    2314             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L100
    2315           0 :     fd_vote_authorize_with_seed_args_t * args = &instruction->inner.authorize_with_seed;
    2316             : 
    2317           0 :     rc = process_authorize_with_seed_instruction( ctx,
    2318           0 :                                                   &me,
    2319           0 :                                                   &args->new_authority,
    2320           0 :                                                   args->authorization_type,
    2321           0 :                                                   &args->current_authority_derived_key_owner,
    2322           0 :                                                   args->current_authority_derived_key_seed,
    2323           0 :                                                   args->current_authority_derived_key_seed_len );
    2324             : 
    2325           0 :     break;
    2326           0 :   }
    2327             : 
    2328             :   /* AuthorizeCheckedWithSeed
    2329             :    *
    2330             :    * Instruction:
    2331             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L131
    2332             :    *
    2333             :    * Processor:
    2334             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L111
    2335             :    */
    2336           0 :   case fd_vote_instruction_enum_authorize_checked_with_seed: {
    2337           0 :     fd_vote_authorize_checked_with_seed_args_t const * args =
    2338           0 :         &instruction->inner.authorize_checked_with_seed;
    2339             : 
    2340             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L112
    2341           0 :     if( FD_UNLIKELY( ctx->instr->acct_cnt < 4 ) ) {
    2342           0 :       rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
    2343           0 :       break;
    2344           0 :     }
    2345             : 
    2346             :     // https://github.com/anza-xyz/agave/blob/v2.1.14/programs/vote/src/vote_processor.rs#L99-L100
    2347           0 :     fd_pubkey_t const * new_authority = NULL;
    2348           0 :     rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 3UL, &new_authority );
    2349           0 :     if( FD_UNLIKELY( rc ) ) return rc;
    2350             : 
    2351             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L116
    2352           0 :     if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( ctx->instr, 3, &rc ) ) ) {
    2353             :       /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
    2354           0 :       if( FD_UNLIKELY( !!rc ) ) break;
    2355             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L117
    2356           0 :       rc = FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
    2357           0 :       break;
    2358           0 :     }
    2359             : 
    2360             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L119
    2361           0 :     rc = process_authorize_with_seed_instruction( ctx,
    2362           0 :                                                   &me,
    2363           0 :                                                   new_authority,
    2364           0 :                                                   args->authorization_type,
    2365           0 :                                                   &args->current_authority_derived_key_owner,
    2366           0 :                                                   args->current_authority_derived_key_seed,
    2367           0 :                                                   args->current_authority_derived_key_seed_len );
    2368             : 
    2369           0 :     break;
    2370           0 :   }
    2371             : 
    2372             :   /* UpdateValidatorIdentity
    2373             :    *
    2374             :    * Instruction:
    2375             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L65
    2376             :    *
    2377             :    * Processor:
    2378             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L130
    2379             :    */
    2380           0 :   case fd_vote_instruction_enum_update_validator_identity: {
    2381             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L131
    2382           0 :     if( FD_UNLIKELY( ctx->instr->acct_cnt < 2 ) ) {
    2383           0 :       rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
    2384           0 :       break;
    2385           0 :     }
    2386             : 
    2387             :     // https://github.com/anza-xyz/agave/blob/v2.1.14/programs/vote/src/vote_processor.rs#L118-L120
    2388           0 :     fd_pubkey_t const * node_pubkey = NULL;
    2389           0 :     rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 1UL, &node_pubkey );
    2390           0 :     if( FD_UNLIKELY( rc ) ) return rc;
    2391             : 
    2392             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L135
    2393           0 :     rc = update_validator_identity( &me, node_pubkey, signers, ctx );
    2394             : 
    2395           0 :     break;
    2396           0 :   }
    2397             : 
    2398             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L142
    2399           0 :   case fd_vote_instruction_enum_update_commission: {
    2400             : 
    2401             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L149
    2402           0 :     fd_epoch_schedule_t epoch_schedule_;
    2403           0 :     fd_epoch_schedule_t const * epoch_schedule = fd_sysvar_cache_epoch_schedule_read( ctx->sysvar_cache, &epoch_schedule_ );
    2404           0 :     if( FD_UNLIKELY( !epoch_schedule ) ) {
    2405           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    2406           0 :     }
    2407             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L150
    2408             : 
    2409           0 :     fd_sol_sysvar_clock_t clock_;
    2410           0 :     fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
    2411           0 :     if( FD_UNLIKELY( !clock ) ) {
    2412           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    2413           0 :     }
    2414             : 
    2415             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L145
    2416           0 :     rc = update_commission( &me,
    2417           0 :                             instruction->inner.update_commission,
    2418           0 :                             signers,
    2419           0 :                             epoch_schedule,
    2420           0 :                             clock,
    2421           0 :                             ctx );
    2422             : 
    2423           0 :     break;
    2424           0 :   }
    2425             : 
    2426             :   /* Vote
    2427             :    *
    2428             :    * Instruction:
    2429             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L49
    2430             :    */
    2431           0 :   case fd_vote_instruction_enum_vote:;
    2432             :     /* clang-format off */
    2433           0 :     __attribute__((fallthrough));
    2434             :     /* clang-format on */
    2435             : 
    2436             :   /* VoteSwitch
    2437             :    *
    2438             :    * Instruction:
    2439             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L81
    2440             :    *
    2441             :    * Processor:
    2442             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L154
    2443             :    */
    2444           0 :   case fd_vote_instruction_enum_vote_switch: {
    2445           0 :     if( FD_FEATURE_ACTIVE_BANK( ctx->bank, deprecate_legacy_vote_ixs ) ) {
    2446           0 :       return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
    2447           0 :     }
    2448             : 
    2449           0 :     fd_vote_t * vote;
    2450           0 :     if( instruction->discriminant == fd_vote_instruction_enum_vote ) {
    2451           0 :       vote = &instruction->inner.vote;
    2452           0 :     } else if( instruction->discriminant == fd_vote_instruction_enum_vote_switch ) {
    2453           0 :       vote = &instruction->inner.vote_switch.vote;
    2454           0 :     } else {
    2455           0 :       __builtin_unreachable();
    2456           0 :     }
    2457             : 
    2458             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L155
    2459           0 :     int err;
    2460           0 :     err = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_slot_hashes_id );
    2461           0 :     if( FD_UNLIKELY( err ) ) return err;
    2462             : 
    2463           0 :     if( FD_UNLIKELY( !fd_sysvar_cache_slot_hashes_is_valid( ctx->sysvar_cache ) ) ) {
    2464           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    2465           0 :     }
    2466             : 
    2467             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L157
    2468           0 :     err = fd_sysvar_instr_acct_check( ctx, 2, &fd_sysvar_clock_id );
    2469           0 :     if( FD_UNLIKELY( err ) ) return err;
    2470           0 :     fd_sol_sysvar_clock_t clock_;
    2471           0 :     fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
    2472           0 :     if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    2473             : 
    2474           0 :     fd_slot_hash_t const * slot_hashes = fd_sysvar_cache_slot_hashes_join_const( ctx->sysvar_cache ); /* guaranteed to succeed */
    2475           0 :     rc = process_vote_with_account( &me, slot_hashes, clock, vote, signers, ctx );
    2476           0 :     fd_sysvar_cache_slot_hashes_leave_const( ctx->sysvar_cache, slot_hashes );
    2477             : 
    2478           0 :     break;
    2479           0 :   }
    2480             : 
    2481             :   /* UpdateVoteState
    2482             :    *
    2483             :    * Instruction:
    2484             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L100
    2485             :    */
    2486           0 :   case fd_vote_instruction_enum_update_vote_state:;
    2487             :     /* clang-format off */
    2488           0 :     __attribute__((fallthrough));
    2489             :     /* clang-format on */
    2490             : 
    2491             :   /* UpdateVoteStateSwitch
    2492             :    *
    2493             :    * Instruction:
    2494             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L107
    2495             :    *
    2496             :    * Processor:
    2497             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L169
    2498             :    */
    2499           0 :   case fd_vote_instruction_enum_update_vote_state_switch: {
    2500           0 :     if( FD_FEATURE_ACTIVE_BANK( ctx->bank, deprecate_legacy_vote_ixs ) ) {
    2501           0 :       return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
    2502           0 :     }
    2503             : 
    2504           0 :     fd_vote_state_update_t * vote_state_update;
    2505           0 :     switch( instruction->discriminant ) {
    2506           0 :     case fd_vote_instruction_enum_update_vote_state:
    2507           0 :       vote_state_update = &instruction->inner.update_vote_state;
    2508           0 :       break;
    2509           0 :     case fd_vote_instruction_enum_update_vote_state_switch:
    2510           0 :       vote_state_update = &instruction->inner.update_vote_state_switch.vote_state_update;
    2511           0 :       break;
    2512           0 :     default:
    2513           0 :       __builtin_unreachable();
    2514           0 :     }
    2515             : 
    2516             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L171
    2517           0 :     if( FD_LIKELY( !fd_sysvar_cache_slot_hashes_is_valid( ctx->sysvar_cache ) ) ) {
    2518           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    2519           0 :     }
    2520             : 
    2521             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L172
    2522           0 :     fd_sol_sysvar_clock_t clock_;
    2523           0 :     fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
    2524           0 :     if( FD_UNLIKELY( !clock ) )
    2525           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    2526             : 
    2527             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L173
    2528           0 :     fd_slot_hash_t const * slot_hashes = fd_sysvar_cache_slot_hashes_join_const( ctx->sysvar_cache );
    2529           0 :     rc = process_vote_state_update( &me, slot_hashes, clock, vote_state_update, signers, ctx );
    2530           0 :     fd_sysvar_cache_slot_hashes_leave_const( ctx->sysvar_cache, slot_hashes );
    2531             : 
    2532           0 :     break;
    2533           0 :   }
    2534             : 
    2535             :   /* CompactUpdateVoteState
    2536             :    *
    2537             :    * Instruction:
    2538             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L139
    2539             :    *
    2540             :    * Notes:
    2541             :    * - Up to three signers: the vote authority, the authorized withdrawer, and the new authority.
    2542             :    * - Feature gated, but live on mainnet.
    2543             :    */
    2544           0 :   case fd_vote_instruction_enum_compact_update_vote_state:;
    2545           0 :     __attribute__((fallthrough));
    2546             : 
    2547             :   /* CompactUpdateVoteStateSwitch
    2548             :    *
    2549             :    * Instruction:
    2550             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L146
    2551             :    *
    2552             :    * Processor:
    2553             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L183
    2554             :    *
    2555             :    * Notes:
    2556             :    * - Up to three signers: the vote authority, the authorized withdrawer, and the new authority.
    2557             :    * - Feature gated, but live on mainnet.
    2558             :    */
    2559           0 :   case fd_vote_instruction_enum_compact_update_vote_state_switch: {
    2560             :     /* https://github.com/anza-xyz/agave/blob/dc4b9dcbbf859ff48f40d00db824bde063fdafcc/programs/vote/src/vote_processor.rs#L183-L191 */
    2561           0 :     if( FD_FEATURE_ACTIVE_BANK( ctx->bank, deprecate_legacy_vote_ixs ) ) {
    2562           0 :       return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
    2563           0 :     }
    2564             : 
    2565           0 :     fd_compact_vote_state_update_t * vote_state_update = NULL;
    2566           0 :     if( instruction->discriminant == fd_vote_instruction_enum_compact_update_vote_state ) {
    2567           0 :       vote_state_update = &instruction->inner.compact_update_vote_state;
    2568           0 :     } else if( instruction->discriminant ==
    2569           0 :                fd_vote_instruction_enum_compact_update_vote_state_switch ) {
    2570           0 :       vote_state_update =
    2571           0 :           &instruction->inner.compact_update_vote_state_switch.compact_vote_state_update;
    2572           0 :     }
    2573             : 
    2574           0 :     fd_vote_state_update_t vote_update;
    2575           0 :     fd_vote_state_update_new( &vote_update );
    2576           0 :     if( FD_UNLIKELY( !fd_vote_decode_compact_update( vote_state_update, &vote_update, ctx ) ) )
    2577           0 :       return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
    2578             : 
    2579             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L185
    2580           0 :     if( FD_LIKELY( !fd_sysvar_cache_slot_hashes_is_valid( ctx->sysvar_cache ) ) ) {
    2581           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    2582           0 :     }
    2583             : 
    2584           0 :     fd_sol_sysvar_clock_t clock_;
    2585           0 :     fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
    2586           0 :     if( FD_UNLIKELY( !clock ) )
    2587           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    2588             : 
    2589             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L187
    2590           0 :     fd_slot_hash_t const * slot_hashes = fd_sysvar_cache_slot_hashes_join_const( ctx->sysvar_cache ); /* guaranteed to succeed */
    2591           0 :     rc = process_vote_state_update( &me, slot_hashes, clock, &vote_update, signers, ctx );
    2592           0 :     fd_sysvar_cache_slot_hashes_leave_const( ctx->sysvar_cache, slot_hashes );
    2593             : 
    2594           0 :     break;
    2595           0 :   }
    2596             : 
    2597             :   /* TowerSync(Switch)
    2598             :    *
    2599             :    * Instruction:
    2600             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L151-L157
    2601             :    *
    2602             :    * Processor:
    2603             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L196-L215
    2604             :    */
    2605             : 
    2606           0 :   case fd_vote_instruction_enum_tower_sync:
    2607           0 :   case fd_vote_instruction_enum_tower_sync_switch: {
    2608           0 :     fd_tower_sync_t * tower_sync = (instruction->discriminant == fd_vote_instruction_enum_tower_sync)
    2609           0 :         ? &instruction->inner.tower_sync
    2610           0 :         : &instruction->inner.tower_sync_switch.tower_sync;
    2611             : 
    2612           0 :     if( FD_LIKELY( !fd_sysvar_cache_slot_hashes_is_valid( ctx->sysvar_cache ) ) ) {
    2613           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    2614           0 :     }
    2615             : 
    2616           0 :     fd_sol_sysvar_clock_t clock_;
    2617           0 :     fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
    2618           0 :     if( FD_UNLIKELY( !clock ) ) {
    2619           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    2620           0 :     }
    2621             : 
    2622           0 :     fd_slot_hash_t const * slot_hashes = fd_sysvar_cache_slot_hashes_join_const( ctx->sysvar_cache );
    2623           0 :     FD_TEST( slot_hashes );
    2624           0 :     rc = process_tower_sync( &me, slot_hashes, clock, tower_sync, signers, ctx );
    2625           0 :     fd_sysvar_cache_slot_hashes_leave_const( ctx->sysvar_cache, slot_hashes );
    2626             : 
    2627           0 :     break;
    2628           0 :   }
    2629             : 
    2630             :   /* Withdraw
    2631             :    *
    2632             :    * Instruction:
    2633             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L57
    2634             :    *
    2635             :    * Processor:
    2636             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L216
    2637             :    */
    2638           0 :   case fd_vote_instruction_enum_withdraw: {
    2639           0 :     if( FD_UNLIKELY( ctx->instr->acct_cnt < 2 ) ) {
    2640           0 :       rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
    2641           0 :       break;
    2642           0 :     }
    2643           0 :     fd_rent_t rent_;
    2644           0 :     fd_rent_t const * rent_sysvar = fd_sysvar_cache_rent_read( ctx->sysvar_cache, &rent_ );
    2645           0 :     if( FD_UNLIKELY( !rent_sysvar ) )
    2646           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    2647           0 :     fd_sol_sysvar_clock_t clock_;
    2648           0 :     fd_sol_sysvar_clock_t const * clock_sysvar = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
    2649           0 :     if( FD_UNLIKELY( !clock_sysvar ) )
    2650           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    2651             : 
    2652           0 :     rc = withdraw( ctx,
    2653           0 :                    &me,
    2654           0 :                    instruction->inner.withdraw,
    2655           0 :                    1UL,
    2656           0 :                    signers,
    2657           0 :                    rent_sysvar,
    2658           0 :                    clock_sysvar );
    2659             : 
    2660           0 :     break;
    2661           0 :   }
    2662             : 
    2663             :   /* AuthorizeChecked
    2664             :    *
    2665             :    * Instruction:
    2666             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L93
    2667             :    *
    2668             :    * Processor:
    2669             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L234
    2670             :    *
    2671             :    * Notes:
    2672             :    * - Up to three signers: the vote authority, the authorized withdrawer, and the new authority.
    2673             :    * - Feature gated, but live on mainnet.
    2674             :    */
    2675           0 :   case fd_vote_instruction_enum_authorize_checked: {
    2676           0 :     if( FD_UNLIKELY( ctx->instr->acct_cnt < 4 ) ) {
    2677           0 :       rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
    2678           0 :       break;
    2679           0 :     }
    2680             : 
    2681             :     // https://github.com/anza-xyz/agave/blob/v2.1.14/programs/vote/src/vote_processor.rs#L243-L245
    2682           0 :     fd_pubkey_t const * voter_pubkey = NULL;
    2683           0 :     rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 3UL, &voter_pubkey );
    2684           0 :     if( FD_UNLIKELY( rc ) ) return rc;
    2685             : 
    2686             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L239
    2687           0 :     if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( ctx->instr, 3, &rc ) ) ) {
    2688             :       /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
    2689           0 :       if( FD_UNLIKELY( !!rc ) ) break;
    2690             : 
    2691           0 :       rc = FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
    2692           0 :       break;
    2693           0 :     }
    2694             : 
    2695             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L242
    2696           0 :     rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_clock_id );
    2697           0 :     if( FD_UNLIKELY( rc ) ) return rc;
    2698           0 :     fd_sol_sysvar_clock_t clock_;
    2699           0 :     fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
    2700           0 :     if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    2701             : 
    2702           0 :     rc = authorize( &me,
    2703           0 :                     voter_pubkey,
    2704           0 :                     instruction->inner.authorize_checked,
    2705           0 :                     signers,
    2706           0 :                     clock,
    2707           0 :                     ctx );
    2708           0 :     break;
    2709           0 :   }
    2710             : 
    2711           0 :   default:
    2712           0 :     FD_LOG_ERR(( "unsupported vote instruction: %u", instruction->discriminant ));
    2713           0 :   }
    2714             : 
    2715           0 :   return rc;
    2716           0 : }
    2717             : 
    2718             : /**********************************************************************/
    2719             : /* Public API                                                         */
    2720             : /**********************************************************************/
    2721             : 
    2722             : uint
    2723           0 : fd_vote_state_versions_is_correct_and_initialized( fd_txn_account_t * vote_account ) {
    2724             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L885
    2725           0 :   uint data_len_check = fd_txn_account_get_data_len( vote_account )==FD_VOTE_STATE_V3_SZ;
    2726           0 :   uchar test_data[DEFAULT_PRIOR_VOTERS_OFFSET] = {0};
    2727           0 :   uint data_check = memcmp((
    2728           0 :       fd_txn_account_get_data( vote_account )+VERSION_OFFSET), test_data, DEFAULT_PRIOR_VOTERS_OFFSET)!=0;
    2729           0 :   if (data_check && data_len_check) {
    2730           0 :     return 1;
    2731           0 :   }
    2732             : 
    2733             :   // VoteState1_14_11::is_correct_size_and_initialized
    2734             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/vote_state_1_14_11.rs#L58
    2735           0 :   data_len_check = fd_txn_account_get_data_len( vote_account )==FD_VOTE_STATE_V2_SZ;
    2736           0 :   uchar test_data_1_14_11[DEFAULT_PRIOR_VOTERS_OFFSET_1_14_11] = {0};
    2737           0 :   data_check = memcmp( (
    2738           0 :       fd_txn_account_get_data( vote_account )+VERSION_OFFSET), test_data_1_14_11, DEFAULT_PRIOR_VOTERS_OFFSET_1_14_11)!=0;
    2739           0 :   return data_check && data_len_check;
    2740           0 : }
    2741             : 
    2742             : fd_vote_state_versioned_t *
    2743             : fd_vote_get_state( fd_txn_account_t const * self,
    2744           0 :                    uchar *                  mem /* out */ ) {
    2745             : 
    2746           0 :   int err = get_state( self, mem );
    2747           0 :   return err ? NULL : (fd_vote_state_versioned_t *)mem;
    2748           0 : }
    2749             : 
    2750             : void
    2751             : fd_vote_convert_to_current( fd_vote_state_versioned_t * self,
    2752             :                             uchar *                     authorized_voters_mem,
    2753           0 :                             uchar *                     landed_votes_mem ) {
    2754           0 :   convert_to_current( self, authorized_voters_mem, landed_votes_mem );
    2755           0 : }
    2756             : 
    2757             : void
    2758             : fd_vote_store_account( fd_txn_account_t * vote_account,
    2759           0 :                        fd_bank_t *        bank ) {
    2760             : 
    2761           0 :   fd_vote_states_t * vote_states = fd_bank_vote_states_locking_modify( bank );
    2762             : 
    2763           0 :   if( fd_txn_account_get_lamports( vote_account )==0UL ) {
    2764           0 :     fd_vote_states_remove( vote_states, vote_account->pubkey );
    2765           0 :     fd_bank_vote_states_end_locking_modify( bank );
    2766           0 :     return;
    2767           0 :   }
    2768             : 
    2769           0 :   if( !fd_vote_state_versions_is_correct_and_initialized( vote_account ) ) {
    2770           0 :     fd_vote_states_remove( vote_states, vote_account->pubkey );
    2771           0 :     fd_bank_vote_states_end_locking_modify( bank );
    2772           0 :     return;
    2773           0 :   }
    2774             : 
    2775           0 :   fd_vote_states_update_from_account( vote_states,
    2776           0 :                                       vote_account->pubkey,
    2777           0 :                                       fd_txn_account_get_data( vote_account ),
    2778           0 :                                       fd_txn_account_get_data_len( vote_account ) );
    2779           0 :   fd_bank_vote_states_end_locking_modify( bank );
    2780           0 : }

Generated by: LCOV version 1.14