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 1747 0.0 %
Date: 2025-08-05 05:04:49 Functions: 0 64 0.0 %

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

Generated by: LCOV version 1.14