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 1767 0.0 %
Date: 2025-06-30 04:56:04 Functions: 0 64 0.0 %

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

Generated by: LCOV version 1.14