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 1635 0.0 %
Date: 2025-10-16 04:31:23 Functions: 0 59 0.0 %

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

Generated by: LCOV version 1.14