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 1232 0.0 %
Date: 2026-01-23 05:02:40 Functions: 0 24 0.0 %

          Line data    Source code
       1             : #include "fd_vote_program.h"
       2             : #include "../fd_runtime.h"
       3             : #include "../fd_borrowed_account.h"
       4             : #include "../fd_executor.h"
       5             : #include "../fd_pubkey_utils.h"
       6             : #include "../sysvar/fd_sysvar_rent.h"
       7             : #include "../sysvar/fd_sysvar.h"
       8             : #include "../fd_system_ids.h"
       9             : #include "vote/fd_authorized_voters.h"
      10             : #include "vote/fd_vote_common.h"
      11             : #include "vote/fd_vote_lockout.h"
      12             : #include "vote/fd_vote_state_versioned.h"
      13             : #include "vote/fd_vote_state_v3.h"
      14             : #include "vote/fd_vote_state_v4.h"
      15             : 
      16             : #include <limits.h>
      17             : #include <math.h>
      18             : #include <stdio.h>
      19             : #include <string.h>
      20             : 
      21             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L36
      22           0 : #define INITIAL_LOCKOUT 2UL
      23             : 
      24             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L51
      25             : #define VOTE_CREDITS_MAXIMUM_PER_SLOT_OLD 8
      26             : 
      27             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/clock.rs#L147
      28             : #define SLOT_DEFAULT 0UL
      29             : 
      30             : // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/clock.rs#L147
      31             : #define SLOT_MAX ULONG_MAX
      32             : 
      33             : #define ACCOUNTS_MAX 4 /* Vote instructions take in at most 4 accounts */
      34             : 
      35             : #define DEFAULT_COMPUTE_UNITS 2100UL
      36             : 
      37             : /**********************************************************************/
      38             : /* VoteStateHandler                                                   */
      39             : /**********************************************************************/
      40             : 
      41             : /* This is a temporary method in Agave (until the vote state v4 feature
      42             :    is cleaned up) to check the vote state and, in some cases, check
      43             :    if the vote account is uninitialized or not. Initializes a v3 or v4
      44             :    vote account depending on the target version.
      45             :    https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L45-L77 */
      46             : static int
      47             : get_vote_state_handler_checked( fd_borrowed_account_t const * vote_account,
      48             :                                 int                           target_version,
      49             :                                 uchar                         check_initialized,
      50             :                                 uchar *                       vote_state_mem,
      51             :                                 uchar *                       authorized_voters_mem,
      52           0 :                                 uchar *                       landed_votes_mem ) {
      53           0 :   int rc;
      54           0 :   switch( target_version ) {
      55             :     /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L50-L62 */
      56           0 :     case VOTE_STATE_TARGET_VERSION_V3: {
      57           0 :       rc = fd_vote_state_v3_deserialize( vote_account, vote_state_mem, authorized_voters_mem, landed_votes_mem );
      58           0 :       if( FD_UNLIKELY( rc ) ) return rc;
      59             : 
      60           0 :       fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)vote_state_mem;
      61           0 :       if( FD_UNLIKELY( check_initialized && fd_vsv_is_uninitialized( versioned ) ) ) {
      62           0 :         return FD_EXECUTOR_INSTR_ERR_UNINITIALIZED_ACCOUNT;
      63           0 :       }
      64             : 
      65           0 :       return FD_EXECUTOR_INSTR_SUCCESS;
      66           0 :     }
      67             :     /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L63-L75 */
      68           0 :     case VOTE_STATE_TARGET_VERSION_V4: {
      69           0 :       rc = fd_vsv_deserialize( vote_account, vote_state_mem );
      70           0 :       if( FD_UNLIKELY( rc ) ) return rc;
      71             : 
      72           0 :       fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)vote_state_mem;
      73           0 :       if( FD_UNLIKELY( fd_vsv_is_uninitialized( versioned ) ) ) {
      74           0 :         return FD_EXECUTOR_INSTR_ERR_UNINITIALIZED_ACCOUNT;
      75           0 :       }
      76             : 
      77           0 :       rc = fd_vsv_try_convert_to_v4( versioned, vote_account->pubkey, landed_votes_mem );
      78           0 :       if( FD_UNLIKELY( rc ) ) return rc;
      79             : 
      80           0 :       return FD_EXECUTOR_INSTR_SUCCESS;
      81           0 :     }
      82           0 :     default:
      83           0 :       FD_LOG_CRIT(( "unsupported version: %d", target_version ));
      84           0 :   }
      85           0 : }
      86             : 
      87             : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L888-L902 */
      88             : static int
      89             : check_vote_account_length( fd_borrowed_account_t const * vote_account,
      90           0 :                            int                           target_version ) {
      91           0 :   ulong length = fd_borrowed_account_get_data_len( vote_account );
      92           0 :   ulong expected;
      93           0 :   switch( target_version ) {
      94           0 :     case VOTE_STATE_TARGET_VERSION_V3:
      95           0 :       expected = FD_VOTE_STATE_V3_SZ;
      96           0 :       break;
      97           0 :     case VOTE_STATE_TARGET_VERSION_V4:
      98           0 :       expected = FD_VOTE_STATE_V4_SZ;
      99           0 :       break;
     100           0 :     default:
     101           0 :       FD_LOG_CRIT(( "unsupported version: %d", target_version ));
     102           0 :   }
     103           0 :   if( FD_UNLIKELY( length!=expected ) ) {
     104           0 :     return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
     105           0 :   }
     106           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
     107           0 : }
     108             : 
     109             : /* The versioned parameter must point to a buffer that is aligned to
     110             :    FD_VOTE_STATE_VERSIONED_ALIGN and is at least
     111             :    FD_VOTE_STATE_VERSIONED_FOOTPRINT bytes in size.
     112             :    https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/handler.rs#L855-L870 */
     113             : static int
     114             : init_vote_account_state( fd_exec_instr_ctx_t *         ctx,
     115             :                          fd_borrowed_account_t *       vote_account,
     116             :                          fd_vote_state_versioned_t *   versioned,
     117             :                          int                           target_version,
     118             :                          fd_vote_init_t *              vote_init,
     119           0 :                          fd_sol_sysvar_clock_t const * clock ) {
     120             :   /* Reset the object */
     121           0 :   fd_vote_state_versioned_new( versioned );
     122             : 
     123           0 :   switch( target_version ) {
     124           0 :     case VOTE_STATE_TARGET_VERSION_V3:
     125           0 :       fd_vote_program_v3_create_new(
     126           0 :           vote_init,
     127           0 :           clock,
     128           0 :           ctx->runtime->vote_program.init_account.authorized_voters_mem,
     129           0 :           versioned
     130           0 :       );
     131           0 :       return fd_vote_state_v3_set_vote_account_state(
     132           0 :           ctx,
     133           0 :           vote_account,
     134           0 :           versioned,
     135           0 :           ctx->runtime->vote_program.init_account.vote_lockout_mem
     136           0 :       );
     137           0 :     case VOTE_STATE_TARGET_VERSION_V4:
     138           0 :       fd_vote_state_v4_create_new(
     139           0 :           vote_account->pubkey,
     140           0 :           vote_init,
     141           0 :           clock,
     142           0 :           ctx->runtime->vote_program.init_account.authorized_voters_mem,
     143           0 :           versioned
     144           0 :       );
     145           0 :       return fd_vote_state_v4_set_vote_account_state( ctx, vote_account, versioned );
     146           0 :     default:
     147           0 :       FD_LOG_CRIT(( "unsupported version: %d", target_version ));
     148           0 :   }
     149           0 : }
     150             : 
     151             : /**********************************************************************/
     152             : /* mod vote_state                                                    */
     153             : /**********************************************************************/
     154             : 
     155             : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L82-L324 */
     156             : static int
     157             : check_and_filter_proposed_vote_state( fd_exec_instr_ctx_t *       ctx,
     158             :                                       fd_vote_state_versioned_t * versioned,
     159             :                                       fd_vote_lockout_t *         proposed_lockouts,
     160             :                                       uchar *                     proposed_has_root,
     161             :                                       ulong *                     proposed_root,
     162             :                                       fd_hash_t const *           proposed_hash,
     163           0 :                                       fd_slot_hash_t const *      slot_hashes /* deque */ ) {
     164           0 :   fd_landed_vote_t const * votes         = fd_vsv_get_votes( versioned );
     165           0 :   ulong const *            root_slot     = fd_vsv_get_root_slot( versioned );
     166           0 :   uchar                    has_root_slot = !!(root_slot!=NULL);
     167             : 
     168             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L208
     169           0 :   if( FD_UNLIKELY( deq_fd_vote_lockout_t_empty( proposed_lockouts ) ) ) {
     170           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_EMPTY_SLOTS;
     171           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     172           0 :   }
     173           0 :   fd_landed_vote_t const * last_vote = NULL;
     174           0 :   if( !deq_fd_landed_vote_t_empty( votes ) ) {
     175             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L212
     176           0 :     last_vote = deq_fd_landed_vote_t_peek_tail_const( votes );
     177           0 :   }
     178             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L218
     179           0 :   if( FD_LIKELY( last_vote ) ) {
     180           0 :     if( FD_UNLIKELY( deq_fd_vote_lockout_t_peek_tail_const( proposed_lockouts )->slot <=
     181           0 :                      last_vote->lockout.slot ) ) {
     182           0 :       ctx->txn_out->err.custom_err = FD_VOTE_ERR_VOTE_TOO_OLD;
     183           0 :       return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     184           0 :     }
     185           0 :   }
     186             : 
     187             :   /* must be nonempty, checked above */
     188           0 :   ulong last_vote_state_update_slot = deq_fd_vote_lockout_t_peek_tail_const( proposed_lockouts )->slot;
     189             : 
     190             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L224
     191           0 :   if( FD_UNLIKELY( deq_fd_slot_hash_t_empty( slot_hashes ) ) ) {
     192           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_MISMATCH;
     193           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     194           0 :   }
     195             : 
     196             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L227
     197           0 :   ulong earliest_slot_hash_in_history = deq_fd_slot_hash_t_peek_tail_const( slot_hashes )->slot;
     198             : 
     199             :   /* Check if the proposed vote is too old to be in the SlotHash history */
     200             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L230
     201           0 :   if( FD_UNLIKELY( last_vote_state_update_slot < earliest_slot_hash_in_history ) ) {
     202           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_VOTE_TOO_OLD;
     203           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     204           0 :   }
     205             : 
     206             :   /* Check if the proposed root is too old */
     207             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L237
     208           0 :   if( *proposed_has_root ) {
     209           0 :     ulong const proposed_root_ = *proposed_root;
     210             :     /* If the new proposed root `R` is less than the earliest slot hash in the history
     211             :        such that we cannot verify whether the slot was actually was on this fork, set
     212             :        the root to the latest vote in the current vote that's less than R. */
     213             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L242
     214           0 :     if( proposed_root_ < earliest_slot_hash_in_history ) {
     215           0 :       *proposed_has_root = has_root_slot;
     216           0 :       if( has_root_slot ) {
     217           0 :         *proposed_root   = *root_slot;
     218           0 :       }
     219           0 :       for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init_rev( votes );
     220           0 :                                              !deq_fd_landed_vote_t_iter_done_rev( votes, iter );
     221           0 :                                        iter = deq_fd_landed_vote_t_iter_prev( votes, iter ) ) {
     222             :         /* Ensure we're iterating from biggest to smallest vote in the
     223             :            current vote state */
     224           0 :         fd_landed_vote_t const * vote = deq_fd_landed_vote_t_iter_ele_const( votes, iter );
     225             :         // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L248
     226           0 :         if( vote->lockout.slot <= proposed_root_ ) {
     227           0 :           *proposed_has_root = 1;
     228           0 :           *proposed_root     = vote->lockout.slot;
     229           0 :           break;
     230           0 :         }
     231           0 :       }
     232           0 :     }
     233           0 :   }
     234             : 
     235             :   /* Index into the new proposed vote state's slots, starting with the root if it exists then
     236             :      we use this mutable root to fold checking the root slot into the below loop for performance */
     237           0 :   int   has_root_to_check       = *proposed_has_root;
     238             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L259
     239           0 :   ulong root_to_check           = *proposed_root;
     240           0 :   ulong proposed_lockouts_index = 0UL;
     241           0 :   ulong lockouts_len = deq_fd_vote_lockout_t_cnt( proposed_lockouts );
     242             : 
     243             :   /* Index into the slot_hashes, starting at the oldest known slot hash */
     244             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L264
     245           0 :   ulong slot_hashes_index = deq_fd_slot_hash_t_cnt( slot_hashes );
     246             : 
     247             :   /* proposed_lockouts_indexes_to_filter's size is bounded by the length
     248             :      of the proposed lockouts provided in the instruction data, which is
     249             :      capped at roughly 10KB / sizeof(ulong). */
     250           0 :   ulong proposed_lockouts_indexes_to_filter[ FD_INSTR_DATA_MAX/sizeof(ulong) ];
     251           0 :   ulong filter_index = 0UL;
     252             : 
     253             :   /* Note:
     254             : 
     255             :     1) `vote_state_update.lockouts` is sorted from oldest/smallest vote to newest/largest
     256             :     vote, due to the way votes are applied to the vote state (newest votes
     257             :     pushed to the back).
     258             : 
     259             :     2) Conversely, `slot_hashes` is sorted from newest/largest vote to
     260             :     the oldest/smallest vote.
     261             : 
     262             :     Unlike for vote updates, vote state updates here can't only check votes older than the last vote
     263             :     because have to ensure that every slot is actually part of the history, not just the most
     264             :     recent ones */
     265             : 
     266             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L279
     267           0 :   while( proposed_lockouts_index < lockouts_len && slot_hashes_index > 0 ) {
     268           0 :     ulong proposed_vote_slot =
     269           0 :       fd_ulong_if( has_root_to_check,
     270             :         // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L281
     271           0 :         root_to_check,
     272             :         // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L283
     273           0 :         deq_fd_vote_lockout_t_peek_index_const( proposed_lockouts,
     274           0 :           proposed_lockouts_index )
     275           0 :         ->slot );
     276             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L285
     277           0 :     if( !has_root_to_check && proposed_lockouts_index > 0UL &&
     278           0 :       proposed_vote_slot <=
     279           0 :       deq_fd_vote_lockout_t_peek_index_const(
     280           0 :         proposed_lockouts,
     281           0 :           fd_ulong_checked_sub_expect(
     282           0 :             proposed_lockouts_index,
     283           0 :               1,
     284           0 :               "`proposed_lockouts_index` is positive when checking `SlotsNotOrdered`" ) )
     285           0 :       ->slot ) {
     286             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L293
     287           0 :       ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_NOT_ORDERED;
     288           0 :       return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     289           0 :     }
     290             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L295
     291           0 :     ulong ancestor_slot =
     292           0 :       deq_fd_slot_hash_t_peek_index_const(
     293           0 :         slot_hashes,
     294           0 :           fd_ulong_checked_sub_expect(
     295           0 :             slot_hashes_index,
     296           0 :               1UL,
     297           0 :               "`slot_hashes_index` is positive when computing `ancestor_slot`" ) )
     298           0 :       ->slot;
     299             :     /* Find if this slot in the proposed vote state exists in the SlotHashes history
     300             :        to confirm if it was a valid ancestor on this fork */
     301             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L303
     302           0 :     if( proposed_vote_slot < ancestor_slot ) {
     303           0 :       if( slot_hashes_index == deq_fd_slot_hash_t_cnt( slot_hashes ) ) {
     304             :         /* The vote slot does not exist in the SlotHashes history because it's too old,
     305             :            i.e. older than the oldest slot in the history. */
     306           0 :         if( proposed_vote_slot >= earliest_slot_hash_in_history ) {
     307           0 :           ctx->txn_out->err.custom_err = 0;
     308           0 :           return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     309           0 :         }
     310             :         // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L310
     311           0 :         if( !fd_vote_contains_slot( votes, proposed_vote_slot ) && !has_root_to_check ) {
     312             :           /* If the vote slot is both:
     313             :              1) Too old
     314             :              2) Doesn't already exist in vote state
     315             :              Then filter it out */
     316           0 :           proposed_lockouts_indexes_to_filter[filter_index++] = proposed_lockouts_index;        }
     317             :         // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L318
     318           0 :         if( has_root_to_check ) {
     319           0 :           ulong new_proposed_root = root_to_check;
     320             :           /* 1. Because `root_to_check.is_some()`, then we know that
     321             :              we haven't checked the root yet in this loop, so
     322             :              `proposed_vote_slot` == `new_proposed_root` == `vote_state_update.root` */
     323           0 :           FD_TEST( new_proposed_root == proposed_vote_slot );
     324             :           /* 2. We know from the assert earlier in the function that
     325             :              `proposed_vote_slot < earliest_slot_hash_in_history`,
     326             :              so from 1. we know that `new_proposed_root < earliest_slot_hash_in_history` */
     327           0 :           if( new_proposed_root >= earliest_slot_hash_in_history ) {
     328           0 :             ctx->txn_out->err.custom_err = 0;
     329           0 :             return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     330           0 :           }
     331             : 
     332             :           // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L329
     333           0 :           has_root_to_check = 0;
     334           0 :           root_to_check     = ULONG_MAX;
     335           0 :         } else {
     336             :           // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L331
     337           0 :           proposed_lockouts_index = fd_ulong_checked_add_expect(
     338           0 :             proposed_lockouts_index,
     339           0 :               1,
     340           0 :               "`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` when "
     341           0 :               "`proposed_vote_slot` is too old to be in SlotHashes history" );
     342           0 :         }
     343           0 :         continue;
     344           0 :       } else {
     345             :         /* If the vote slot is new enough to be in the slot history,
     346             :            but is not part of the slot history, then it must belong to another fork,
     347             :            which means this vote state update is invalid. */
     348             :         // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L340
     349           0 :         if( has_root_to_check ) {
     350           0 :           ctx->txn_out->err.custom_err = FD_VOTE_ERR_ROOT_ON_DIFFERENT_FORK;
     351           0 :           return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     352           0 :         } else {
     353           0 :           ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_MISMATCH;
     354           0 :           return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     355           0 :         }
     356           0 :       }
     357           0 :     } else if( proposed_vote_slot > ancestor_slot ) {
     358             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L347
     359             : 
     360             :       /* Decrement `slot_hashes_index` to find newer slots in the SlotHashes history */
     361           0 :       slot_hashes_index = fd_ulong_checked_sub_expect(
     362           0 :         slot_hashes_index,
     363           0 :           1,
     364           0 :           "`slot_hashes_index` is positive when finding newer slots in SlotHashes history" );
     365           0 :       continue;
     366           0 :     } else {
     367             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L354
     368             : 
     369             :       /* Once the slot in `vote_state_update.lockouts` is found, bump to the next slot
     370             :          in `vote_state_update.lockouts` and continue. If we were checking the root,
     371             :          start checking the vote state instead. */
     372           0 :       if( has_root_to_check ) {
     373           0 :         has_root_to_check = 0;
     374           0 :         root_to_check     = ULONG_MAX;
     375           0 :       } else {
     376           0 :         proposed_lockouts_index = fd_ulong_checked_add_expect(
     377           0 :           proposed_lockouts_index,
     378           0 :             1,
     379           0 :             "`proposed_lockouts_index` is bounded by `MAX_LOCKOUT_HISTORY` "
     380           0 :             "when match is found in SlotHashes history" );
     381           0 :         slot_hashes_index = fd_ulong_checked_sub_expect(
     382           0 :           slot_hashes_index,
     383           0 :             1,
     384           0 :             "`slot_hashes_index` is positive when match is found in SlotHashes history" );
     385           0 :       }
     386           0 :     }
     387           0 :   }
     388             : 
     389             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L372
     390           0 :   if( proposed_lockouts_index != deq_fd_vote_lockout_t_cnt( proposed_lockouts ) ) {
     391             :     /* The last vote slot in the update did not exist in SlotHashes */
     392           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_MISMATCH;
     393           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     394           0 :   }
     395             : 
     396             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L401
     397           0 :   if( memcmp( &deq_fd_slot_hash_t_peek_index_const( slot_hashes, slot_hashes_index )->hash,
     398           0 :       proposed_hash,
     399           0 :       sizeof( fd_hash_t ) ) != 0 ) {
     400             :     /* This means the newest vote in the slot has a match that
     401             :        doesn't match the expected hash for that slot on this fork */
     402           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_HASH_MISMATCH;
     403           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     404           0 :   }
     405             : 
     406             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L418
     407             :   /* Filter out the irrelevant votes */
     408           0 :   proposed_lockouts_index = 0UL;
     409           0 :   ulong filter_votes_index = deq_fd_vote_lockout_t_cnt( proposed_lockouts );
     410             : 
     411             :   /* We need to iterate backwards here because proposed_lockouts_indexes_to_filter[ i ] is a
     412             :      strictly increasing value. Forward iterating can lead to the proposed lockout indicies to get
     413             :      shifted leading to popping the wrong proposed lockouts or out of bounds accessing. We need
     414             :      to be sure of handling underflow in this case. */
     415             : 
     416           0 :   for( ulong i=filter_index; i>0UL && filter_votes_index>0UL; i-- ) {
     417           0 :     proposed_lockouts_index = i - 1UL;
     418           0 :     if( FD_UNLIKELY( proposed_lockouts_indexes_to_filter[ proposed_lockouts_index ]>=filter_votes_index ) ) {
     419           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
     420           0 :     }
     421             : 
     422           0 :     deq_fd_vote_lockout_t_pop_idx_tail( proposed_lockouts, proposed_lockouts_indexes_to_filter[ proposed_lockouts_index ] );
     423           0 :     filter_votes_index--;
     424           0 :   }
     425             : 
     426           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
     427           0 : }
     428             : 
     429             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L440
     430             : static int
     431             : check_slots_are_valid( fd_exec_instr_ctx_t *       ctx,
     432             :                        fd_vote_state_versioned_t * versioned,
     433             :                        ulong const *               vote_slots,
     434             :                        fd_hash_t const *           vote_hash,
     435           0 :                        fd_slot_hash_t const *      slot_hashes /* deque */ ) {
     436           0 :   ulong i              = 0;
     437           0 :   ulong j              = deq_fd_slot_hash_t_cnt( slot_hashes );
     438           0 :   ulong vote_slots_len = deq_ulong_cnt( vote_slots );
     439             : 
     440             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L462
     441           0 :   while( i < vote_slots_len && j > 0 ) {
     442           0 :     ulong const * last_voted_slot_ = fd_vsv_get_last_voted_slot( versioned );
     443           0 :     if( FD_UNLIKELY( last_voted_slot_ &&
     444           0 :                      *deq_ulong_peek_index_const( vote_slots, i ) <= *last_voted_slot_ ) ) {
     445             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L469
     446           0 :       i = fd_ulong_checked_add_expect(
     447           0 :           i, 1, "`i` is bounded by `MAX_LOCKOUT_HISTORY` when finding larger slots" );
     448           0 :       continue;
     449           0 :     }
     450             : 
     451             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L476
     452           0 :     if( FD_UNLIKELY(
     453           0 :             *deq_ulong_peek_index_const( vote_slots, i ) !=
     454           0 :             deq_fd_slot_hash_t_peek_index_const( slot_hashes,
     455           0 :               fd_ulong_checked_sub_expect( j, 1, "`j` is positive" ) )
     456           0 :                 ->slot ) ) {
     457           0 :       j = fd_ulong_checked_sub_expect( j, 1, "`j` is positive when finding newer slots" );
     458           0 :       continue;
     459           0 :     }
     460             : 
     461             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L486
     462           0 :     i = fd_ulong_checked_add_expect(
     463           0 :         i, 1, "`i` is bounded by `MAX_LOCKOUT_HISTORY` when hash is found" );
     464           0 :     j = fd_ulong_checked_sub_expect( j, 1, "`j` is positive when hash is found" );
     465           0 :   }
     466             : 
     467             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L494
     468           0 :   if( FD_UNLIKELY( j == deq_fd_slot_hash_t_cnt( slot_hashes ) ) ) {
     469           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_VOTE_TOO_OLD;
     470           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     471           0 :   }
     472           0 :   if( FD_UNLIKELY( i != vote_slots_len ) ) {
     473           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_MISMATCH;
     474           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     475           0 :   }
     476             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L514
     477           0 :   if( FD_UNLIKELY( 0 != memcmp( &deq_fd_slot_hash_t_peek_index_const( slot_hashes, j )->hash,
     478           0 :                                 vote_hash,
     479           0 :                                 32UL ) ) ) {
     480           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_HASH_MISMATCH;
     481           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     482           0 :   }
     483           0 :   return 0;
     484           0 : }
     485             : 
     486             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L565
     487             : static int
     488             : process_new_vote_state( fd_exec_instr_ctx_t *       ctx,
     489             :                         fd_vote_state_versioned_t * versioned,
     490             :                         fd_landed_vote_t *          new_state,
     491             :                         int                         has_new_root,
     492             :                         ulong                       new_root,
     493             :                         int                         has_timestamp,
     494             :                         long                        timestamp,
     495             :                         ulong                       epoch,
     496           0 :                         ulong                       current_slot ) {
     497           0 :   int rc;
     498           0 :   fd_landed_vote_t * votes         = fd_vsv_get_votes_mutable( versioned );
     499           0 :   ulong const *      root_slot     = fd_vsv_get_root_slot( versioned );
     500           0 :   uchar              has_root_slot = !!(root_slot!=NULL);
     501             : 
     502           0 :   FD_TEST( !deq_fd_landed_vote_t_empty( new_state ) );
     503             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L575
     504           0 :   if( FD_UNLIKELY( deq_fd_landed_vote_t_cnt( new_state ) > MAX_LOCKOUT_HISTORY ) ) {
     505           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_TOO_MANY_VOTES;
     506           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     507           0 :   };
     508             : 
     509             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L579
     510           0 :   if( FD_UNLIKELY( has_new_root && has_root_slot ) ) {
     511             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L581
     512           0 :     if( FD_UNLIKELY( new_root<*root_slot ) ) {
     513           0 :       ctx->txn_out->err.custom_err = FD_VOTE_ERR_ROOT_ROLL_BACK;
     514           0 :       return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     515           0 :     }
     516           0 :   } else if( FD_UNLIKELY( !has_new_root && has_root_slot ) ) {
     517             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L586
     518           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_ROOT_ROLL_BACK;
     519           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     520           0 :   } else {
     521             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L588
     522             :     /* no-op */
     523           0 :   }
     524             : 
     525           0 :   fd_landed_vote_t * previous_vote = NULL;
     526           0 :   for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( new_state );
     527           0 :        !deq_fd_landed_vote_t_iter_done( new_state, iter );
     528           0 :        iter = deq_fd_landed_vote_t_iter_next( new_state, iter ) ) {
     529           0 :     fd_landed_vote_t * vote = deq_fd_landed_vote_t_iter_ele( new_state, iter );
     530           0 :     if( FD_LIKELY( vote->lockout.confirmation_count == 0 ) ) {
     531           0 :       ctx->txn_out->err.custom_err = FD_VOTE_ERR_ZERO_CONFIRMATIONS;
     532           0 :       return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     533           0 :     } else if( FD_UNLIKELY( vote->lockout.confirmation_count > MAX_LOCKOUT_HISTORY ) ) {
     534           0 :       ctx->txn_out->err.custom_err = FD_VOTE_ERR_CONFIRMATION_TOO_LARGE;
     535           0 :       return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     536           0 :     } else if( FD_LIKELY( has_new_root ) ) {
     537           0 :       if( FD_UNLIKELY( vote->lockout.slot <= new_root && new_root != SLOT_DEFAULT ) ) {
     538           0 :         ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOT_SMALLER_THAN_ROOT;
     539           0 :         return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     540           0 :       }
     541           0 :     }
     542             : 
     543           0 :     if( FD_LIKELY( previous_vote ) ) {
     544           0 :       if( FD_UNLIKELY( previous_vote->lockout.slot >= vote->lockout.slot ) ) {
     545           0 :         ctx->txn_out->err.custom_err = FD_VOTE_ERR_SLOTS_NOT_ORDERED;
     546           0 :         return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     547           0 :       } else if( FD_UNLIKELY( previous_vote->lockout.confirmation_count <=
     548           0 :                               vote->lockout.confirmation_count ) ) {
     549           0 :         ctx->txn_out->err.custom_err = FD_VOTE_ERR_CONFIRMATIONS_NOT_ORDERED;
     550           0 :         return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     551           0 :       } else if( FD_UNLIKELY( vote->lockout.slot >
     552           0 :                               fd_vote_lockout_last_locked_out_slot( &previous_vote->lockout ) ) ) {
     553           0 :         ctx->txn_out->err.custom_err = FD_VOTE_ERR_NEW_VOTE_STATE_LOCKOUT_MISMATCH;
     554           0 :         return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     555           0 :       }
     556           0 :     }
     557           0 :     previous_vote = vote;
     558           0 :   }
     559             : 
     560           0 :   ulong current_vote_state_index = 0;
     561           0 :   ulong new_vote_state_index     = 0;
     562             : 
     563             :   /* Accumulate credits earned by newly rooted slots.  The behavior changes with
     564             :      timely_vote_credits: prior to this feature, there was a bug that counted a new root slot as 1
     565             :      credit even if it had never been voted on. timely_vote_credits fixes this bug by only awarding
     566             :      credits for slots actually voted on and finalized. */
     567             : 
     568             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L635
     569             : 
     570             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L641
     571           0 :   ulong earned_credits      = 0;
     572             : 
     573           0 :   if( FD_LIKELY( has_new_root ) ) {
     574           0 :     for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( votes );
     575           0 :                                            !deq_fd_landed_vote_t_iter_done( votes, iter );
     576           0 :                                      iter = deq_fd_landed_vote_t_iter_next( votes, iter ) ) {
     577           0 :       fd_landed_vote_t const * current_vote = deq_fd_landed_vote_t_iter_ele_const( votes, iter );
     578             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L647
     579           0 :       if( FD_UNLIKELY( current_vote->lockout.slot <= new_root ) ) {
     580             :         // this is safe because we're inside if has_new_root
     581           0 :         earned_credits = fd_ulong_checked_add_expect(
     582           0 :             fd_vote_credits_for_vote_at_index(
     583           0 :                 votes,
     584           0 :                 current_vote_state_index
     585           0 :             ),
     586           0 :             earned_credits,
     587           0 :             "`earned_credits` does not overflow" );
     588           0 :         current_vote_state_index = fd_ulong_checked_add_expect(
     589           0 :             current_vote_state_index,
     590           0 :             1,
     591           0 :             "`current_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` "
     592           0 :             "when processing new root" );
     593           0 :         continue;
     594           0 :       }
     595           0 :       break;
     596           0 :     }
     597           0 :   }
     598             : 
     599             :   // For any slots newly added to the new vote state, the vote latency of that slot is not provided by the
     600             :   // vote instruction contents, but instead is computed from the actual latency of the vote
     601             :   // instruction. This prevents other validators from manipulating their own vote latencies within their vote states
     602             :   // and forcing the rest of the cluster to accept these possibly fraudulent latency values.  If the
     603             :   // timly_vote_credits feature is not enabled then vote latency is set to 0 for new votes.
     604             :   //
     605             :   // For any slot that is in both the new state and the current state, the vote latency of the new state is taken
     606             :   // from the current state.
     607             :   //
     608             :   // Thus vote latencies are set here for any newly vote-on slots when a vote instruction is received.
     609             :   // They are copied into the new vote state after every vote for already voted-on slots.
     610             :   // And when voted-on slots are rooted, the vote latencies stored in the vote state of all the rooted slots is used
     611             :   // to compute credits earned.
     612             :   // All validators compute the same vote latencies because all process the same vote instruction at the
     613             :   // same slot, and the only time vote latencies are ever computed is at the time that their slot is first voted on;
     614             :   // after that, the latencies are retained unaltered until the slot is rooted.
     615             : 
     616             :   // All the votes in our current vote state that are missing from the new vote state
     617             :   // must have been expired by later votes. Check that the lockouts match this assumption.
     618             : 
     619             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L686
     620           0 :   while( current_vote_state_index < deq_fd_landed_vote_t_cnt( votes ) &&
     621           0 :          new_vote_state_index < deq_fd_landed_vote_t_cnt( new_state ) ) {
     622           0 :     fd_landed_vote_t const * current_vote = deq_fd_landed_vote_t_peek_index_const( votes, current_vote_state_index );
     623             : 
     624             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L690
     625           0 :     fd_landed_vote_t * new_vote =
     626           0 :         deq_fd_landed_vote_t_peek_index( new_state, new_vote_state_index );
     627             : 
     628             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L696
     629           0 :     if( FD_LIKELY( current_vote->lockout.slot < new_vote->lockout.slot ) ) {
     630             :       /* The agave implementation of calculating the last locked out
     631             :          slot does not calculate a min between the current vote's
     632             :          confirmation count and max lockout history. The reason we do
     633             :          this is to make sure that the fuzzers continue working:
     634             :          the max lockout history can not be > MAX_LOCKOUT_HISTORY. */
     635           0 :       ulong confirmation_count   = fd_ulong_min( current_vote->lockout.confirmation_count, MAX_LOCKOUT_HISTORY );
     636           0 :       ulong last_locked_out_slot = fd_ulong_sat_add( current_vote->lockout.slot,
     637           0 :                                                      (ulong)pow( INITIAL_LOCKOUT, (double)confirmation_count ) );
     638             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L697
     639           0 :       if( last_locked_out_slot >= new_vote->lockout.slot ) {
     640             :         // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L698
     641           0 :         ctx->txn_out->err.custom_err = FD_VOTE_ERR_LOCKOUT_CONFLICT;
     642           0 :         return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     643           0 :       }
     644             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L700
     645           0 :       current_vote_state_index =
     646           0 :           fd_ulong_checked_add_expect( current_vote_state_index,
     647           0 :                                        1,
     648           0 :                                        "`current_vote_state_index` is bounded by "
     649           0 :                                        "`MAX_LOCKOUT_HISTORY` when slot is less than proposed" );
     650             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L704
     651           0 :     } else if( FD_UNLIKELY( current_vote->lockout.slot == new_vote->lockout.slot ) ) {
     652             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L707
     653           0 :       if( new_vote->lockout.confirmation_count < current_vote->lockout.confirmation_count ) {
     654           0 :         ctx->txn_out->err.custom_err = FD_VOTE_ERR_CONFIRMATION_ROLL_BACK;
     655           0 :         return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     656           0 :       }
     657             : 
     658             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L712
     659           0 :       new_vote->latency = deq_fd_landed_vote_t_peek_index_const( votes, current_vote_state_index )->latency;
     660             : 
     661             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L714
     662           0 :       current_vote_state_index =
     663           0 :           fd_ulong_checked_add_expect( current_vote_state_index,
     664           0 :                                        1,
     665           0 :                                        "`current_vote_state_index` is bounded by "
     666           0 :                                        "`MAX_LOCKOUT_HISTORY` when slot is equal to proposed" );
     667             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L717
     668           0 :       new_vote_state_index =
     669           0 :           fd_ulong_checked_add_expect( new_vote_state_index,
     670           0 :                                        1,
     671           0 :                                        "`new_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` "
     672           0 :                                        "when slot is equal to proposed" );
     673           0 :     } else {
     674             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L722
     675           0 :       new_vote_state_index =
     676           0 :           fd_ulong_checked_add_expect( new_vote_state_index,
     677           0 :                                        1,
     678           0 :                                        "`new_vote_state_index` is bounded by `MAX_LOCKOUT_HISTORY` "
     679           0 :                                        "when slot is greater than proposed" );
     680           0 :     }
     681           0 :   }
     682             : 
     683             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L737
     684           0 :   for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( new_state );
     685           0 :         !deq_fd_landed_vote_t_iter_done( new_state, iter );
     686           0 :         iter = deq_fd_landed_vote_t_iter_next( new_state, iter ) ) {
     687           0 :     fd_landed_vote_t * new_vote = deq_fd_landed_vote_t_iter_ele( new_state, iter );
     688             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L738
     689           0 :     if( FD_UNLIKELY( new_vote->latency == 0 ) ) {
     690             :       // this is unlikely because as validators upgrade, it should converge to the new vote state
     691             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L739
     692           0 :       new_vote->latency = fd_vote_compute_vote_latency( new_vote->lockout.slot, current_slot );
     693           0 :     }
     694           0 :   }
     695             : 
     696             :   // doesn't matter what the value of slot if `is_some = 0` i.e. `Option::None`
     697             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L744
     698           0 :   int both_none = !has_root_slot && !has_new_root;
     699           0 :   if( ( !both_none && ( has_root_slot!=has_new_root ||
     700           0 :                         *root_slot!=new_root ) ) ) {
     701           0 :     fd_vsv_increment_credits( versioned, epoch, earned_credits );
     702           0 :   }
     703             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L750
     704           0 :   if( FD_LIKELY( has_timestamp ) ) {
     705             :     /* new_state asserted nonempty at function beginning */
     706           0 :     if( FD_UNLIKELY( deq_fd_landed_vote_t_empty( new_state ) ) ) {
     707           0 :       FD_LOG_CRIT(( "invariant violation: landed votes is empty" ));
     708           0 :     }
     709           0 :     ulong last_slot = deq_fd_landed_vote_t_peek_tail( new_state )->lockout.slot;
     710           0 :     rc              = fd_vsv_process_timestamp( ctx, versioned, last_slot, timestamp );
     711           0 :     if( FD_UNLIKELY( rc ) ) { return rc; }
     712           0 :     fd_vsv_process_timestamp( ctx, versioned, last_slot, timestamp );
     713           0 :   }
     714             : 
     715             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L754
     716           0 :   fd_vsv_set_root_slot( versioned, has_new_root ? &new_root : NULL );
     717             : 
     718             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L755
     719           0 :   deq_fd_landed_vote_t_remove_all( votes );
     720           0 :   for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( new_state );
     721           0 :        !deq_fd_landed_vote_t_iter_done( new_state, iter );
     722           0 :        iter = deq_fd_landed_vote_t_iter_next( new_state, iter ) ) {
     723           0 :     fd_landed_vote_t * landed_vote = deq_fd_landed_vote_t_iter_ele( new_state, iter );
     724           0 :     deq_fd_landed_vote_t_push_tail_wrap( votes, *landed_vote );
     725           0 :   }
     726             : 
     727           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
     728           0 : }
     729             : 
     730             : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L716-L759 */
     731             : static int
     732             : authorize( fd_exec_instr_ctx_t *         ctx,
     733             :            fd_borrowed_account_t *       vote_account,
     734             :            int                           target_version,
     735             :            fd_pubkey_t const *           authorized,
     736             :            fd_vote_authorize_t           vote_authorize,
     737             :            fd_pubkey_t const *           signers[static FD_TXN_SIG_MAX],
     738           0 :            fd_sol_sysvar_clock_t const * clock ) {
     739             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L724-L727 */
     740           0 :   int rc = get_vote_state_handler_checked(
     741           0 :       vote_account,
     742           0 :       target_version,
     743           0 :       0,
     744           0 :       ctx->runtime->vote_program.authorize.vote_state_mem,
     745           0 :       ctx->runtime->vote_program.authorize.authorized_voters_mem,
     746           0 :       ctx->runtime->vote_program.authorize.landed_votes_mem
     747           0 :   );
     748           0 :   if( FD_UNLIKELY( rc ) ) return rc;
     749             : 
     750             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L729-L756 */
     751           0 :   fd_vote_state_versioned_t * vote_state_versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.authorize.vote_state_mem;
     752           0 :   switch( vote_authorize.discriminant ) {
     753             : 
     754             :     /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L730-L750 */
     755           0 :     case fd_vote_authorize_enum_voter: {
     756             : 
     757             :       /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L731-L732 */
     758           0 :       int authorized_withdrawer_signer = !fd_vote_verify_authorized_signer(
     759           0 :           fd_vsv_get_authorized_withdrawer( vote_state_versioned ),
     760           0 :           signers
     761           0 :       );
     762             : 
     763             :       /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L737-L740 */
     764           0 :       ulong target_epoch;
     765           0 :       rc = fd_ulong_checked_add( clock->leader_schedule_epoch, 1UL, &target_epoch );
     766           0 :       if( FD_UNLIKELY( rc!=FD_EXECUTOR_INSTR_SUCCESS ) ) {
     767           0 :         return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA;
     768           0 :       }
     769             : 
     770             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L866
     771           0 :       rc = fd_vsv_set_new_authorized_voter(
     772           0 :           ctx,
     773           0 :           vote_state_versioned,
     774           0 :           authorized,
     775           0 :           clock->epoch,
     776           0 :           target_epoch,
     777           0 :           authorized_withdrawer_signer,
     778           0 :           signers
     779           0 :       );
     780           0 :       if( FD_UNLIKELY( rc ) ) return rc;
     781           0 :       break;
     782           0 :     }
     783             : 
     784             :     /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L751-L756 */
     785           0 :     case fd_vote_authorize_enum_withdrawer: {
     786           0 :       rc = fd_vote_verify_authorized_signer(
     787           0 :           fd_vsv_get_authorized_withdrawer( vote_state_versioned ),
     788           0 :           signers
     789           0 :       );
     790           0 :       if( FD_UNLIKELY( rc ) ) return rc;
     791           0 :       fd_vsv_set_authorized_withdrawer( vote_state_versioned, authorized );
     792           0 :       break;
     793           0 :     }
     794           0 :     default:
     795           0 :       FD_LOG_CRIT(( "unsupported vote_authorize discriminant: %u", vote_authorize.discriminant ));
     796           0 :   }
     797             : 
     798             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L758 */
     799           0 :   return fd_vsv_set_vote_account_state(
     800           0 :       ctx,
     801           0 :       vote_account,
     802           0 :       vote_state_versioned,
     803           0 :       ctx->runtime->vote_program.authorize.vote_lockout_mem
     804           0 :   );
     805           0 : }
     806             : 
     807             : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L761-L785 */
     808             : static int
     809             : update_validator_identity( fd_exec_instr_ctx_t *   ctx,
     810             :                            int                     target_version,
     811             :                            fd_borrowed_account_t * vote_account,
     812             :                            fd_pubkey_t const *     node_pubkey,
     813           0 :                            fd_pubkey_t const *     signers[static FD_TXN_SIG_MAX] ) {
     814             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L768-L771 */
     815           0 :   int rc = get_vote_state_handler_checked(
     816           0 :       vote_account,
     817           0 :       target_version,
     818           0 :       0,
     819           0 :       ctx->runtime->vote_program.update_validator_identity.vote_state_mem,
     820           0 :       ctx->runtime->vote_program.update_validator_identity.authorized_voters_mem,
     821           0 :       ctx->runtime->vote_program.update_validator_identity.landed_votes_mem
     822           0 :   );
     823           0 :   if( FD_UNLIKELY( rc ) ) return rc;
     824             : 
     825           0 :   fd_vote_state_versioned_t * vote_state_versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.update_validator_identity.vote_state_mem;
     826             : 
     827             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L774 */
     828           0 :   rc = fd_vote_verify_authorized_signer(
     829           0 :       fd_vsv_get_authorized_withdrawer( vote_state_versioned ),
     830           0 :       signers
     831           0 :   );
     832           0 :   if( FD_UNLIKELY( rc ) ) return rc;
     833             : 
     834             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L777 */
     835           0 :   rc = fd_vote_verify_authorized_signer( node_pubkey, signers );
     836           0 :   if( FD_UNLIKELY( rc ) ) return rc;
     837             : 
     838             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L779 */
     839           0 :   fd_vsv_set_node_pubkey( vote_state_versioned, node_pubkey );
     840             : 
     841             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L782 */
     842           0 :   fd_vsv_set_block_revenue_collector( vote_state_versioned, node_pubkey );
     843             : 
     844             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L784 */
     845           0 :   return fd_vsv_set_vote_account_state(
     846           0 :       ctx,
     847           0 :       vote_account,
     848           0 :       vote_state_versioned,
     849           0 :       ctx->runtime->vote_program.update_validator_identity.vote_lockout_mem
     850           0 :   );
     851           0 : }
     852             : 
     853             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L971
     854             : static int
     855           0 : is_commission_update_allowed( ulong slot, fd_epoch_schedule_t const * epoch_schedule ) {
     856           0 :   if( FD_LIKELY( epoch_schedule->slots_per_epoch > 0UL ) ) {
     857           0 :     ulong relative_slot = fd_ulong_sat_sub( slot, epoch_schedule->first_normal_slot );
     858             :     // TODO underflow and overflow edge cases in addition to div by 0
     859           0 :     relative_slot %= epoch_schedule->slots_per_epoch;
     860           0 :     return fd_ulong_sat_mul( relative_slot, 2 ) <= epoch_schedule->slots_per_epoch;
     861           0 :   } else {
     862           0 :     return 1;
     863           0 :   }
     864           0 : }
     865             : 
     866             : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L787-L818 */
     867             : static int
     868             : update_commission( fd_exec_instr_ctx_t *         ctx,
     869             :                    int                           target_version,
     870             :                    fd_borrowed_account_t *       vote_account,
     871             :                    uchar                         commission,
     872             :                    fd_pubkey_t const *           signers[static FD_TXN_SIG_MAX],
     873             :                    fd_epoch_schedule_t const *   epoch_schedule,
     874           0 :                    fd_sol_sysvar_clock_t const * clock ) {
     875             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L796-L799 */
     876           0 :   int rc         = 0;
     877           0 :   int get_vsv_rc = get_vote_state_handler_checked(
     878           0 :       vote_account,
     879           0 :       target_version,
     880           0 :       false,
     881           0 :       ctx->runtime->vote_program.update_commission.vote_state_mem,
     882           0 :       ctx->runtime->vote_program.update_commission.authorized_voters_mem,
     883           0 :       ctx->runtime->vote_program.update_commission.landed_votes_mem
     884           0 :   );
     885             : 
     886             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L800-L804 */
     887           0 :   fd_vote_state_versioned_t * vote_state_versioned           = NULL;
     888           0 :   int                         enforce_commission_update_rule = 1;
     889           0 :   if( FD_LIKELY( get_vsv_rc==FD_EXECUTOR_INSTR_SUCCESS ) ) {
     890           0 :     vote_state_versioned           = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.update_commission.vote_state_mem;
     891           0 :     enforce_commission_update_rule = (commission>fd_vsv_get_commission( vote_state_versioned ));
     892           0 :   }
     893             : 
     894             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L806-L808 */
     895           0 :   if( FD_UNLIKELY( enforce_commission_update_rule && !is_commission_update_allowed( clock->slot, epoch_schedule ) ) ) {
     896           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_COMMISSION_UPDATE_TOO_LATE;
     897           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     898           0 :   }
     899             : 
     900             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L810 */
     901           0 :   if( FD_UNLIKELY( get_vsv_rc ) ) return get_vsv_rc;
     902             : 
     903             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L813 */
     904           0 :   rc = fd_vote_verify_authorized_signer(
     905           0 :       fd_vsv_get_authorized_withdrawer( vote_state_versioned ),
     906           0 :       signers
     907           0 :   );
     908           0 :   if( FD_UNLIKELY( rc ) ) return rc;
     909             : 
     910             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L815 */
     911           0 :   fd_vsv_set_commission( vote_state_versioned, commission );
     912             : 
     913             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L817 */
     914           0 :   return fd_vsv_set_vote_account_state(
     915           0 :       ctx,
     916           0 :       vote_account,
     917           0 :       vote_state_versioned,
     918           0 :       ctx->runtime->vote_program.update_commission.vote_lockout_mem
     919           0 :   );
     920           0 : }
     921             : 
     922             : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L848C8-L903 */
     923             : static int
     924             : withdraw( fd_exec_instr_ctx_t *         ctx,
     925             :           fd_borrowed_account_t *       vote_account,
     926             :           int                           target_version,
     927             :           ulong                         lamports,
     928             :           ushort                        to_account_index,
     929             :           fd_pubkey_t const *           signers[static FD_TXN_SIG_MAX],
     930             :           fd_rent_t const *             rent_sysvar,
     931           0 :           fd_sol_sysvar_clock_t const * clock ) {
     932             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L860-L863 */
     933           0 :   int rc = get_vote_state_handler_checked(
     934           0 :       vote_account,
     935           0 :       target_version,
     936           0 :       0,
     937           0 :       ctx->runtime->vote_program.withdraw.vote_state_mem,
     938           0 :       ctx->runtime->vote_program.withdraw.authorized_voters_mem,
     939           0 :       ctx->runtime->vote_program.withdraw.landed_votes_mem
     940           0 :   );
     941           0 :   if( FD_UNLIKELY( rc ) ) return rc;
     942             : 
     943           0 :   fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.withdraw.vote_state_mem;
     944             : 
     945             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L865 */
     946           0 :   rc = fd_vote_verify_authorized_signer(
     947           0 :       fd_vsv_get_authorized_withdrawer( versioned ),
     948           0 :       signers
     949           0 :   );
     950           0 :   if( FD_UNLIKELY( rc ) ) return rc;
     951             : 
     952             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L867-L870 */
     953           0 :   ulong vote_account_lamports = fd_borrowed_account_get_lamports( vote_account );
     954           0 :   if( FD_UNLIKELY( lamports>vote_account_lamports ) ) {
     955           0 :     return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
     956           0 :   }
     957           0 :   ulong remaining_balance = vote_account_lamports-lamports;
     958             : 
     959             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L872-L896 */
     960           0 :   if( FD_UNLIKELY( remaining_balance==0UL ) ) {
     961             :     /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L873-L883 */
     962           0 :     fd_vote_epoch_credits_t const * epoch_credits = fd_vsv_get_epoch_credits( versioned );
     963           0 :     ulong                           last_epoch_with_credits;
     964           0 :     int                             reject_active_vote_account_close = 0;
     965           0 :     if( FD_LIKELY( !deq_fd_vote_epoch_credits_t_empty( epoch_credits ) ) ) {
     966           0 :       ulong current_epoch              = clock->epoch;
     967           0 :       last_epoch_with_credits          = deq_fd_vote_epoch_credits_t_peek_tail_const( epoch_credits )->epoch;
     968           0 :       reject_active_vote_account_close = fd_ulong_sat_sub( current_epoch, last_epoch_with_credits )<2UL;
     969           0 :     }
     970             : 
     971             :     /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L885-L890 */
     972           0 :     if( FD_UNLIKELY( reject_active_vote_account_close ) ) {
     973           0 :       ctx->txn_out->err.custom_err = FD_VOTE_ERR_ACTIVE_VOTE_ACCOUNT_CLOSE;
     974           0 :       return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
     975           0 :     } else {
     976           0 :       rc = fd_vsv_deinitialize_vote_account_state(
     977           0 :           ctx,
     978           0 :           vote_account,
     979           0 :           target_version,
     980           0 :           ctx->runtime->vote_program.withdraw.vote_lockout_mem
     981           0 :       );
     982           0 :       if( FD_UNLIKELY( rc ) ) return rc;
     983           0 :     }
     984           0 :   } else {
     985             :     /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L892-L895 */
     986           0 :     ulong min_rent_exempt_balance = fd_rent_exempt_minimum_balance( rent_sysvar, fd_borrowed_account_get_data_len( vote_account ) );
     987           0 :     if( FD_UNLIKELY( remaining_balance<min_rent_exempt_balance ) ) {
     988           0 :       return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
     989           0 :     }
     990           0 :   }
     991             : 
     992             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L898 */
     993           0 :   rc = fd_borrowed_account_checked_sub_lamports( vote_account, lamports );
     994           0 :   if( FD_UNLIKELY( rc ) ) return rc;
     995             : 
     996             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L899 */
     997           0 :   fd_borrowed_account_drop( vote_account );
     998             : 
     999             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L900 */
    1000           0 :   fd_guarded_borrowed_account_t to = {0};
    1001           0 :   FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, to_account_index, &to );
    1002             : 
    1003             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L901 */
    1004           0 :   rc = fd_borrowed_account_checked_add_lamports( &to, lamports );
    1005           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1006             : 
    1007           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
    1008           0 : }
    1009             : 
    1010             : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L638-L651 */
    1011             : static int
    1012             : process_vote_unfiltered( fd_exec_instr_ctx_t *       ctx,
    1013             :                          fd_vote_state_versioned_t * versioned,
    1014             :                          ulong *                     vote_slots,
    1015             :                          fd_vote_t const *           vote,
    1016             :                          fd_slot_hash_t const *      slot_hashes, /* deque */
    1017             :                          ulong                       epoch,
    1018           0 :                          ulong                       current_slot ) {
    1019           0 :   int rc;
    1020             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L770
    1021           0 :   rc = check_slots_are_valid( ctx, versioned, vote_slots, &vote->hash, slot_hashes );
    1022           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1023           0 :   for( deq_ulong_iter_t iter = deq_ulong_iter_init( vote_slots );
    1024           0 :        !deq_ulong_iter_done( vote_slots, iter );
    1025           0 :        iter = deq_ulong_iter_next( vote_slots, iter ) ) {
    1026           0 :     ulong * ele = deq_ulong_iter_ele( vote_slots, iter );
    1027             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L772
    1028           0 :     fd_vsv_process_next_vote_slot( versioned, *ele, epoch, current_slot );
    1029           0 :   }
    1030           0 :   return 0;
    1031           0 : }
    1032             : 
    1033             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L783
    1034             : static int
    1035             : process_vote( fd_exec_instr_ctx_t *       ctx,
    1036             :               fd_vote_state_versioned_t * versioned,
    1037             :               fd_vote_t const *           vote,
    1038             :               fd_slot_hash_t const *      slot_hashes, /* deque */
    1039             :               ulong                       epoch,
    1040           0 :               ulong                       current_slot ) {
    1041             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L792
    1042           0 :   if( FD_UNLIKELY( deq_ulong_empty( vote->slots ) ) ) {
    1043           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_EMPTY_SLOTS;
    1044           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1045           0 :   }
    1046             : 
    1047             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L795
    1048           0 :   ulong earliest_slot_in_history = 0;
    1049           0 :   if( FD_UNLIKELY( !deq_fd_slot_hash_t_empty( slot_hashes ) ) ) {
    1050           0 :     earliest_slot_in_history = deq_fd_slot_hash_t_peek_tail_const( slot_hashes )->slot;
    1051           0 :   }
    1052             : 
    1053             :   /* We know that the size of the vote_slots is bounded by the number of
    1054             :      slots that can fit inside of an instruction.  A very loose bound is
    1055             :      assuming that the entire transaction is just filled with a vote
    1056             :      slot deque (1232 bytes per transaction/8 bytes per slot) == 154
    1057             :      slots.  The footprint of a deque is as follows:
    1058             :      fd_ulong_align_up( fd_ulong_align_up( 32UL, alignof(DEQUE_T) ) + sizeof(DEQUE_T)*max, alignof(DEQUE_(private_t)) );
    1059             :      So, the footprint in our case is:
    1060             :      fd_ulong_align_up( fd_ulong_align_up( 32UL, alignof(ulong) ) + sizeof(ulong)*154, alignof(DEQUE_(private_t)) );
    1061             :      Which is equal to
    1062             :      fd_ulong_align_up( 32UL + 154 * 8UL, 8UL ) = 1264UL; */
    1063           0 :   #define VOTE_SLOTS_MAX             (FD_TXN_MTU/sizeof(ulong))
    1064           0 :   #define VOTE_SLOTS_DEQUE_FOOTPRINT (1264UL )
    1065           0 :   #define VOTE_SLOTS_DEQUE_ALIGN     (8UL)
    1066           0 :   FD_TEST( deq_ulong_footprint( VOTE_SLOTS_MAX ) == VOTE_SLOTS_DEQUE_FOOTPRINT );
    1067           0 :   FD_TEST( deq_ulong_align()                     == 8UL );
    1068           0 :   FD_TEST( deq_ulong_cnt( vote->slots )          <= VOTE_SLOTS_MAX );
    1069           0 :   uchar * vote_slots_mem[ VOTE_SLOTS_DEQUE_FOOTPRINT ] __attribute__((aligned(VOTE_SLOTS_DEQUE_ALIGN)));
    1070             : 
    1071             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L796
    1072           0 :   ulong * vote_slots = deq_ulong_join( deq_ulong_new( vote_slots_mem, deq_ulong_cnt( vote->slots ) ) );
    1073           0 :   for( deq_ulong_iter_t iter = deq_ulong_iter_init( vote->slots );
    1074           0 :        !deq_ulong_iter_done( vote->slots, iter );
    1075           0 :        iter = deq_ulong_iter_next( vote->slots, iter ) ) {
    1076           0 :     ulong * ele = deq_ulong_iter_ele( vote->slots, iter );
    1077           0 :     if( FD_UNLIKELY( *ele >= earliest_slot_in_history ) ) {
    1078           0 :       vote_slots = deq_ulong_push_tail_wrap( vote_slots, *ele );
    1079           0 :     }
    1080           0 :   }
    1081             : 
    1082             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L802
    1083           0 :   if( FD_UNLIKELY( deq_ulong_cnt( vote_slots ) == 0 ) ) {
    1084           0 :     ctx->txn_out->err.custom_err = FD_VOTE_ERR_VOTES_TOO_OLD_ALL_FILTERED;
    1085           0 :     return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1086           0 :   }
    1087             : 
    1088             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L805
    1089           0 :   return process_vote_unfiltered(
    1090           0 :       ctx,
    1091           0 :       versioned,
    1092           0 :       vote_slots,
    1093           0 :       vote,
    1094           0 :       slot_hashes,
    1095           0 :       epoch,
    1096           0 :       current_slot
    1097           0 :   );
    1098           0 : }
    1099             : 
    1100             : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L905-L926 */
    1101             : static int
    1102             : initialize_account( fd_exec_instr_ctx_t *         ctx,
    1103             :                     fd_borrowed_account_t *       vote_account,
    1104             :                     int                           target_version,
    1105             :                     fd_vote_init_t *              vote_init,
    1106             :                     fd_pubkey_t const *           signers[static FD_TXN_SIG_MAX],
    1107           0 :                     fd_sol_sysvar_clock_t const * clock ) {
    1108           0 :   int rc;
    1109             : 
    1110             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L915 */
    1111           0 :   rc = check_vote_account_length( vote_account, target_version );
    1112           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1113             : 
    1114             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L916 */
    1115           0 :   rc = fd_vsv_get_state( vote_account->meta, ctx->runtime->vote_program.init_account.vote_state_mem );
    1116           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1117           0 :   fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.init_account.vote_state_mem;
    1118             : 
    1119             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L918-L920 */
    1120           0 :   if( FD_UNLIKELY( !fd_vsv_is_uninitialized( versioned ) ) ) {
    1121           0 :     return FD_EXECUTOR_INSTR_ERR_ACC_ALREADY_INITIALIZED;
    1122           0 :   }
    1123             : 
    1124             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L923 */
    1125           0 :   rc = fd_vote_verify_authorized_signer( &vote_init->node_pubkey, signers );
    1126           0 :   if( FD_UNLIKELY( rc ) ) {
    1127           0 :     return rc;
    1128           0 :   }
    1129             : 
    1130             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L925 */
    1131           0 :   return init_vote_account_state( ctx, vote_account, versioned, target_version, vote_init, clock );
    1132           0 : }
    1133             : 
    1134             : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L928-L953 */
    1135             : static int
    1136             : process_vote_with_account( fd_exec_instr_ctx_t *         ctx,
    1137             :                            fd_borrowed_account_t *       vote_account,
    1138             :                            int                           target_version,
    1139             :                            fd_slot_hash_t const *        slot_hashes, /* deque */
    1140             :                            fd_sol_sysvar_clock_t const * clock,
    1141             :                            fd_vote_t *                   vote,
    1142           0 :                            fd_pubkey_t const *           signers[static FD_TXN_SIG_MAX] ) {
    1143             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L936-L939 */
    1144           0 :   int rc = get_vote_state_handler_checked(
    1145           0 :       vote_account,
    1146           0 :       target_version,
    1147           0 :       1,
    1148           0 :       ctx->runtime->vote_program.process_vote.vote_state_mem,
    1149           0 :       ctx->runtime->vote_program.process_vote.authorized_voters_mem,
    1150           0 :       ctx->runtime->vote_program.process_vote.landed_votes_mem
    1151           0 :   );
    1152           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1153             : 
    1154           0 :   fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.process_vote.vote_state_mem;
    1155             : 
    1156             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L941 */
    1157           0 :   fd_pubkey_t * authorized_voter = NULL;
    1158           0 :   rc = fd_authorized_voters_get_and_update_authorized_voter( versioned, clock->epoch, &authorized_voter );
    1159           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1160             : 
    1161             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L942 */
    1162           0 :   rc = fd_vote_verify_authorized_signer( authorized_voter, signers );
    1163           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1164             : 
    1165             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L944 */
    1166           0 :   rc = process_vote( ctx, versioned, vote, slot_hashes, clock->epoch, clock->slot );
    1167           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1168             : 
    1169             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L945-L951 */
    1170           0 :   if( FD_LIKELY( vote->has_timestamp ) ) {
    1171             :     /* Calling max() on an empty iterator returns None */
    1172           0 :     if( FD_UNLIKELY( deq_ulong_cnt( vote->slots )==0 ) ) {
    1173           0 :       ctx->txn_out->err.custom_err = FD_VOTE_ERR_EMPTY_SLOTS;
    1174           0 :       return FD_EXECUTOR_INSTR_ERR_CUSTOM_ERR;
    1175           0 :     }
    1176             : 
    1177             :     /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L948 */
    1178           0 :     ulong max = 0UL;
    1179           0 :     for( deq_ulong_iter_t iter = deq_ulong_iter_init( vote->slots );
    1180           0 :                                 !deq_ulong_iter_done( vote->slots, iter );
    1181           0 :                           iter = deq_ulong_iter_next( vote->slots, iter ) ) {
    1182           0 :       ulong * ele = deq_ulong_iter_ele( vote->slots, iter );
    1183           0 :       max         = fd_ulong_max( max, *ele );
    1184           0 :     }
    1185             : 
    1186             :     /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L950 */
    1187           0 :     rc = fd_vsv_process_timestamp( ctx, versioned, max, vote->timestamp );
    1188           0 :     if( FD_UNLIKELY( rc ) ) return rc;
    1189           0 :   }
    1190             : 
    1191             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L952 */
    1192           0 :   return fd_vsv_set_vote_account_state(
    1193           0 :       ctx,
    1194           0 :       vote_account,
    1195           0 :       versioned,
    1196           0 :       ctx->runtime->vote_program.process_vote.vote_lockout_mem
    1197           0 :   );
    1198           0 : }
    1199             : 
    1200             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1156
    1201             : static int
    1202             : do_process_vote_state_update( fd_exec_instr_ctx_t *       ctx,
    1203             :                               fd_vote_state_versioned_t * versioned,
    1204             :                               fd_slot_hash_t const *      slot_hashes, /* deque */
    1205             :                               ulong                       epoch,
    1206             :                               ulong                       slot,
    1207           0 :                               fd_vote_state_update_t *    vote_state_update ) {
    1208           0 :   int rc;
    1209             : 
    1210             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1164
    1211           0 :   rc = check_and_filter_proposed_vote_state(
    1212           0 :       ctx,
    1213           0 :       versioned,
    1214           0 :       vote_state_update->lockouts,
    1215           0 :       &vote_state_update->has_root,
    1216           0 :       &vote_state_update->root,
    1217           0 :       &vote_state_update->hash,
    1218           0 :       slot_hashes
    1219           0 :   );
    1220           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1221             : 
    1222             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1177
    1223           0 :   fd_landed_vote_t * landed_votes = deq_fd_landed_vote_t_join( deq_fd_landed_vote_t_new( ctx->runtime->vote_program.process_vote.vs_update_landed_votes_mem, deq_fd_vote_lockout_t_cnt( vote_state_update->lockouts ) ) );
    1224           0 :   for( deq_fd_vote_lockout_t_iter_t iter =
    1225           0 :            deq_fd_vote_lockout_t_iter_init( vote_state_update->lockouts );
    1226           0 :        !deq_fd_vote_lockout_t_iter_done( vote_state_update->lockouts, iter );
    1227           0 :        iter = deq_fd_vote_lockout_t_iter_next( vote_state_update->lockouts, iter ) ) {
    1228           0 :     fd_vote_lockout_t * lockout =
    1229           0 :         deq_fd_vote_lockout_t_iter_ele( vote_state_update->lockouts, iter );
    1230           0 :     deq_fd_landed_vote_t_push_tail_wrap( landed_votes,
    1231           0 :                                     ( fd_landed_vote_t ){ .latency = 0, .lockout = *lockout } );
    1232           0 :   }
    1233             : 
    1234             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_state/mod.rs#L1171
    1235           0 :   return process_new_vote_state(
    1236           0 :       ctx,
    1237           0 :       versioned,
    1238           0 :       landed_votes,
    1239           0 :       vote_state_update->has_root,
    1240           0 :       vote_state_update->root,
    1241           0 :       vote_state_update->has_timestamp,
    1242           0 :       vote_state_update->timestamp,
    1243           0 :       epoch,
    1244           0 :       slot
    1245           0 :   );
    1246           0 : }
    1247             : 
    1248             : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L955-L979 */
    1249             : static int
    1250             : process_vote_state_update( fd_exec_instr_ctx_t *         ctx,
    1251             :                            fd_borrowed_account_t *       vote_account,
    1252             :                            int                           target_version,
    1253             :                            fd_slot_hash_t const *        slot_hashes,
    1254             :                            fd_sol_sysvar_clock_t const * clock,
    1255             :                            fd_vote_state_update_t *      vote_state_update,
    1256           0 :                            fd_pubkey_t const *           signers[static FD_TXN_SIG_MAX] ) {
    1257             : 
    1258             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L963-L966 */
    1259           0 :   int rc = get_vote_state_handler_checked(
    1260           0 :       vote_account,
    1261           0 :       target_version,
    1262           0 :       1,
    1263           0 :       ctx->runtime->vote_program.process_vote.vote_state_mem,
    1264           0 :       ctx->runtime->vote_program.process_vote.authorized_voters_mem,
    1265           0 :       ctx->runtime->vote_program.process_vote.landed_votes_mem
    1266           0 :   );
    1267           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1268             : 
    1269           0 :   fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.process_vote.vote_state_mem;
    1270             : 
    1271             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L968 */
    1272           0 :   fd_pubkey_t * authorized_voter = NULL;
    1273           0 :   rc = fd_authorized_voters_get_and_update_authorized_voter( versioned, clock->epoch, &authorized_voter );
    1274           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1275             : 
    1276             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L969 */
    1277           0 :   rc = fd_vote_verify_authorized_signer( authorized_voter, signers );
    1278           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1279             : 
    1280             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L971-L977 */
    1281           0 :   rc = do_process_vote_state_update(
    1282           0 :       ctx,
    1283           0 :       versioned,
    1284           0 :       slot_hashes,
    1285           0 :       clock->epoch,
    1286           0 :       clock->slot,
    1287           0 :       vote_state_update
    1288           0 :   );
    1289           0 :   if( FD_UNLIKELY( rc ) ) {
    1290           0 :     return rc;
    1291           0 :   }
    1292             : 
    1293             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L978 */
    1294           0 :   return fd_vsv_set_vote_account_state(
    1295           0 :       ctx,
    1296           0 :       vote_account,
    1297           0 :       versioned,
    1298           0 :       ctx->runtime->vote_program.process_vote.vote_lockout_mem
    1299           0 :   );
    1300           0 : }
    1301             : 
    1302             : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1035-L1061 */
    1303             : static int
    1304             : do_process_tower_sync( fd_exec_instr_ctx_t *       ctx,
    1305             :                        fd_vote_state_versioned_t * versioned,
    1306             :                        fd_slot_hash_t const *      slot_hashes, /* deque */
    1307             :                        ulong                       epoch,
    1308             :                        ulong                       slot,
    1309           0 :                        fd_tower_sync_t *           tower_sync ) {
    1310             : 
    1311             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1042-L1048 */
    1312           0 :   int rc = check_and_filter_proposed_vote_state(
    1313           0 :       ctx,
    1314           0 :       versioned,
    1315           0 :       tower_sync->lockouts,
    1316           0 :       &tower_sync->has_root,
    1317           0 :       &tower_sync->root,
    1318           0 :       &tower_sync->hash,
    1319           0 :       slot_hashes
    1320           0 :   );
    1321           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1322             : 
    1323             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1049-L1060 */
    1324           0 :   return process_new_vote_state(
    1325           0 :       ctx,
    1326           0 :       versioned,
    1327           0 :       fd_vote_lockout_landed_votes_from_lockouts( tower_sync->lockouts, ctx->runtime->vote_program.tower_sync.tower_sync_landed_votes_mem ),
    1328           0 :       tower_sync->has_root,
    1329           0 :       tower_sync->root,
    1330           0 :       tower_sync->has_timestamp,
    1331           0 :       tower_sync->timestamp,
    1332           0 :       epoch,
    1333           0 :       slot
    1334           0 :   );
    1335           0 : }
    1336             : 
    1337             : /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1009-L1033 */
    1338             : static int
    1339             : process_tower_sync( fd_exec_instr_ctx_t *         ctx,
    1340             :                     fd_borrowed_account_t *       vote_account,
    1341             :                     int                           target_version,
    1342             :                     fd_slot_hash_t const *        slot_hashes, /* deque */
    1343             :                     fd_sol_sysvar_clock_t const * clock,
    1344             :                     fd_tower_sync_t *             tower_sync,
    1345           0 :                     fd_pubkey_t const *           signers[static FD_TXN_SIG_MAX] ) {
    1346             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1017-L1020 */
    1347           0 :   int rc = get_vote_state_handler_checked(
    1348           0 :      vote_account,
    1349           0 :      target_version,
    1350           0 :      1,
    1351           0 :      ctx->runtime->vote_program.tower_sync.vote_state_mem,
    1352           0 :      ctx->runtime->vote_program.tower_sync.authorized_voters_mem,
    1353           0 :      ctx->runtime->vote_program.tower_sync.vote_state_landed_votes_mem
    1354           0 :   );
    1355           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1356             : 
    1357           0 :   fd_vote_state_versioned_t * versioned = (fd_vote_state_versioned_t *)ctx->runtime->vote_program.tower_sync.vote_state_mem;
    1358             : 
    1359             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1022 */
    1360           0 :   fd_pubkey_t * authorized_voter = NULL;
    1361           0 :   rc = fd_authorized_voters_get_and_update_authorized_voter( versioned, clock->epoch, &authorized_voter );
    1362           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1363             : 
    1364             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1023 */
    1365           0 :   rc = fd_vote_verify_authorized_signer( authorized_voter, signers );
    1366           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1367             : 
    1368             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1025-L1031 */
    1369           0 :   rc = do_process_tower_sync(
    1370           0 :       ctx,
    1371           0 :       versioned,
    1372           0 :       slot_hashes,
    1373           0 :       clock->epoch,
    1374           0 :       clock->slot,
    1375           0 :       tower_sync
    1376           0 :   );
    1377           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1378             : 
    1379             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_state/mod.rs#L1032 */
    1380           0 :   return fd_vsv_set_vote_account_state(
    1381           0 :       ctx,
    1382           0 :       vote_account,
    1383           0 :       versioned,
    1384           0 :       ctx->runtime->vote_program.tower_sync.vote_lockout_mem
    1385           0 :   );
    1386           0 : }
    1387             : 
    1388             : /**********************************************************************/
    1389             : /* FD-only encoders / decoders (doesn't map directly to Labs impl)    */
    1390             : /**********************************************************************/
    1391             : 
    1392             : int
    1393             : fd_vote_decode_compact_update( fd_compact_vote_state_update_t * compact_update,
    1394             :                                fd_vote_state_update_t *         vote_update,
    1395           0 :                                fd_exec_instr_ctx_t const *      ctx ) {
    1396             :   // Taken from:
    1397             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/state/mod.rs#L954
    1398           0 :   if( compact_update->root != ULONG_MAX ) {
    1399           0 :     vote_update->has_root = 1;
    1400           0 :     vote_update->root     = compact_update->root;
    1401           0 :   } else {
    1402           0 :     vote_update->has_root = 0;
    1403           0 :     vote_update->root     = ULONG_MAX;
    1404           0 :   }
    1405             : 
    1406           0 :   ulong lockouts_len = compact_update->lockouts_len;
    1407           0 :   ulong lockouts_max = fd_ulong_max( lockouts_len, MAX_LOCKOUT_HISTORY );
    1408             : 
    1409           0 :   vote_update->lockouts = deq_fd_vote_lockout_t_join( deq_fd_vote_lockout_t_new( ctx->runtime->vote_program.process_vote.compact_vs_lockout_mem, lockouts_max ) );
    1410           0 :   ulong slot            = fd_ulong_if( vote_update->has_root, vote_update->root, 0 );
    1411             : 
    1412           0 :   for( ulong i=0; i < lockouts_len; ++i ) {
    1413           0 :     fd_vote_lockout_t * elem = deq_fd_vote_lockout_t_push_tail_nocopy( vote_update->lockouts );
    1414           0 :     fd_vote_lockout_new( elem );
    1415             : 
    1416           0 :     fd_lockout_offset_t * lock_offset = &compact_update->lockouts[i];
    1417             : 
    1418           0 :     ulong next_slot;
    1419           0 :     if( FD_UNLIKELY( __builtin_uaddl_overflow( slot, lock_offset->offset, &next_slot ) ) )
    1420           0 :       return 0;
    1421             : 
    1422           0 :     elem->slot = slot        = next_slot;
    1423           0 :     elem->confirmation_count = (uint)lock_offset->confirmation_count;
    1424           0 :   }
    1425             : 
    1426           0 :   vote_update->hash          = compact_update->hash;
    1427           0 :   vote_update->has_timestamp = compact_update->has_timestamp;
    1428           0 :   vote_update->timestamp     = compact_update->timestamp;
    1429             : 
    1430           0 :   return 1;
    1431           0 : }
    1432             : 
    1433             : /**********************************************************************/
    1434             : /* mod vote_processor                                                 */
    1435             : /**********************************************************************/
    1436             : 
    1437             : /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L21-L51 */
    1438             : static int
    1439             : process_authorize_with_seed_instruction( /* invoke_context */
    1440             :                                          fd_exec_instr_ctx_t *   ctx,
    1441             :                                          int                     target_version,
    1442             :                                          fd_borrowed_account_t * vote_account,
    1443             :                                          fd_pubkey_t const *     new_authority,
    1444             :                                          fd_vote_authorize_t     authorization_type,
    1445             :                                          fd_pubkey_t const *     current_authority_derived_key_owner,
    1446             :                                          uchar const *           current_authority_derived_key_seed,
    1447           0 :                                          ulong                   current_authority_derived_key_seed_len ) {
    1448           0 :   int rc = 0;
    1449             : 
    1450             :   /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L31 */
    1451           0 :   rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_clock_id );
    1452           0 :   if( FD_UNLIKELY( rc ) ) return rc;
    1453             : 
    1454           0 :   fd_sol_sysvar_clock_t clock_;
    1455           0 :   fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
    1456           0 :   if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    1457             : 
    1458           0 :   fd_pubkey_t * expected_authority_keys[FD_TXN_SIG_MAX] = { 0 };
    1459           0 :   fd_pubkey_t   single_signer                        = { 0 };
    1460             : 
    1461             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L30-L42 */
    1462           0 :   if( fd_instr_acc_is_signer_idx( ctx->instr, 2, &rc ) ) {
    1463             : 
    1464             :     /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L31 */
    1465           0 :     fd_pubkey_t const * base_pubkey = NULL;
    1466           0 :     rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 2UL, &base_pubkey );
    1467           0 :     if( FD_UNLIKELY( rc ) ) return rc;
    1468             : 
    1469             :     /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L34-L40 */
    1470           0 :     expected_authority_keys[0] = &single_signer;
    1471           0 :     rc = fd_pubkey_create_with_seed( ctx,
    1472           0 :                                      base_pubkey->uc,
    1473           0 :                                      (char const *)current_authority_derived_key_seed,
    1474           0 :                                      current_authority_derived_key_seed_len,
    1475           0 :                                      current_authority_derived_key_owner->uc,
    1476           0 :                                      /* insert */ expected_authority_keys[0]->uc );
    1477           0 :     if( FD_UNLIKELY( rc ) ) return rc;
    1478           0 :   }
    1479             : 
    1480             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L43-L50 */
    1481           0 :   return authorize(
    1482           0 :       ctx,
    1483           0 :       vote_account,
    1484           0 :       target_version,
    1485           0 :       new_authority,
    1486           0 :       authorization_type,
    1487           0 :       (fd_pubkey_t const **)expected_authority_keys,
    1488           0 :       clock
    1489           0 :   );
    1490           0 : }
    1491             : 
    1492             : /**********************************************************************/
    1493             : /* Entry point for the Vote Program                                   */
    1494             : /**********************************************************************/
    1495             : 
    1496             : // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L57
    1497             : int
    1498           0 : fd_vote_program_execute( fd_exec_instr_ctx_t * ctx ) {
    1499             :   /* FD-specific init */
    1500           0 :   int rc = FD_EXECUTOR_INSTR_SUCCESS;
    1501             : 
    1502             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L57
    1503           0 :   FD_EXEC_CU_UPDATE( ctx, DEFAULT_COMPUTE_UNITS );
    1504             : 
    1505             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L64-L67 */
    1506           0 :   fd_guarded_borrowed_account_t me = {0};
    1507           0 :   FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, 0, &me );
    1508           0 :   if( FD_UNLIKELY( !fd_pubkey_eq( fd_borrowed_account_get_owner( &me ), &fd_solana_vote_program_id ) ) ) {
    1509           0 :     return FD_EXECUTOR_INSTR_ERR_INVALID_ACC_OWNER;
    1510           0 :   }
    1511             : 
    1512             :   /* https://github.com/anza-xyz/agave/blob/v3.1.1/programs/vote/src/vote_processor.rs#L69-L74 */
    1513           0 :   int target_version = FD_FEATURE_ACTIVE_BANK( ctx->bank, vote_state_v4 ) ? VOTE_STATE_TARGET_VERSION_V4 : VOTE_STATE_TARGET_VERSION_V3;
    1514             : 
    1515             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L69
    1516           0 :   fd_pubkey_t const * signers[FD_TXN_SIG_MAX] = { 0 };
    1517           0 :   fd_exec_instr_ctx_get_signers( ctx, signers );
    1518             : 
    1519           0 :   uchar __attribute__((aligned(alignof(fd_vote_instruction_t)))) vote_instruction_mem[ FD_VOTE_INSTRUCTION_FOOTPRINT ];
    1520           0 :   fd_vote_instruction_t * instruction = fd_bincode_decode_static_limited_deserialize(
    1521           0 :       vote_instruction,
    1522           0 :       vote_instruction_mem,
    1523           0 :       ctx->instr->data,
    1524           0 :       ctx->instr->data_sz,
    1525           0 :       FD_TXN_MTU,
    1526           0 :       NULL );
    1527           0 :   if( FD_UNLIKELY( !instruction ) ) {
    1528           0 :     return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
    1529           0 :   }
    1530             : 
    1531             :   /* PLEASE PRESERVE SWITCH-CASE ORDERING TO MIRROR LABS IMPL:
    1532             :    */
    1533           0 :   switch( instruction->discriminant ) {
    1534             : 
    1535             :   /* InitializeAccount
    1536             :    *
    1537             :    * Instruction:
    1538             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L32
    1539             :    *
    1540             :    * Processor:
    1541             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L71
    1542             :    */
    1543           0 :   case fd_vote_instruction_enum_initialize_account: {
    1544             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L72
    1545           0 :     rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_rent_id );
    1546           0 :     if( FD_UNLIKELY( rc ) ) return rc;
    1547           0 :     fd_rent_t rent_;
    1548           0 :     fd_rent_t const * rent = fd_sysvar_cache_rent_read( ctx->sysvar_cache, &rent_ );
    1549           0 :     if( FD_UNLIKELY( !rent ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    1550             : 
    1551           0 :     if( FD_UNLIKELY( fd_borrowed_account_get_lamports( &me ) <
    1552           0 :                      fd_rent_exempt_minimum_balance( rent, fd_borrowed_account_get_data_len( &me ) ) ) )
    1553           0 :       return FD_EXECUTOR_INSTR_ERR_INSUFFICIENT_FUNDS;
    1554             : 
    1555             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L76
    1556           0 :     rc = fd_sysvar_instr_acct_check( ctx, 2, &fd_sysvar_clock_id );
    1557           0 :     if( FD_UNLIKELY( rc ) ) return rc;
    1558           0 :     fd_sol_sysvar_clock_t clock_;
    1559           0 :     fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
    1560           0 :     if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    1561             : 
    1562             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L78
    1563           0 :     rc = initialize_account( ctx, &me, target_version, &instruction->inner.initialize_account, signers, clock );
    1564             : 
    1565           0 :     break;
    1566           0 :   }
    1567             : 
    1568             :   /* Authorize
    1569             :    *
    1570             :    * Instruction:
    1571             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L40
    1572             :    *
    1573             :    * Processor:
    1574             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L86
    1575             :    *
    1576             :    * Notes:
    1577             :    * - Up to two signers: the vote authority and the authorized withdrawer.
    1578             :    */
    1579           0 :   case fd_vote_instruction_enum_authorize: {
    1580             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L87
    1581           0 :     rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_clock_id );
    1582           0 :     if( FD_UNLIKELY( rc ) ) return rc;
    1583           0 :     fd_sol_sysvar_clock_t clock_;
    1584           0 :     fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
    1585           0 :     if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    1586             : 
    1587             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L89
    1588           0 :     fd_pubkey_t const * voter_pubkey   = &instruction->inner.authorize.pubkey;
    1589           0 :     fd_vote_authorize_t vote_authorize = instruction->inner.authorize.vote_authorize;
    1590             : 
    1591           0 :     rc = authorize( ctx, &me, target_version, voter_pubkey, vote_authorize, signers, clock );
    1592             : 
    1593           0 :     break;
    1594           0 :   }
    1595             : 
    1596             :   /* AuthorizeWithSeed
    1597             :    *
    1598             :    * Instruction:
    1599             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L117
    1600             :    *
    1601             :    * Processor:
    1602             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L98
    1603             :    */
    1604           0 :   case fd_vote_instruction_enum_authorize_with_seed: {
    1605             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L99
    1606           0 :     if( FD_UNLIKELY( ctx->instr->acct_cnt < 3 ) ) {
    1607           0 :       rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
    1608           0 :       break;
    1609           0 :     }
    1610             : 
    1611             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L100
    1612           0 :     fd_vote_authorize_with_seed_args_t * args = &instruction->inner.authorize_with_seed;
    1613             : 
    1614           0 :     rc = process_authorize_with_seed_instruction(
    1615           0 :         ctx,
    1616           0 :         target_version,
    1617           0 :         &me,
    1618           0 :         &args->new_authority,
    1619           0 :         args->authorization_type,
    1620           0 :         &args->current_authority_derived_key_owner,
    1621           0 :         args->current_authority_derived_key_seed,
    1622           0 :         args->current_authority_derived_key_seed_len
    1623           0 :     );
    1624             : 
    1625           0 :     break;
    1626           0 :   }
    1627             : 
    1628             :   /* AuthorizeCheckedWithSeed
    1629             :    *
    1630             :    * Instruction:
    1631             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L131
    1632             :    *
    1633             :    * Processor:
    1634             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L111
    1635             :    */
    1636           0 :   case fd_vote_instruction_enum_authorize_checked_with_seed: {
    1637           0 :     fd_vote_authorize_checked_with_seed_args_t const * args =
    1638           0 :         &instruction->inner.authorize_checked_with_seed;
    1639             : 
    1640             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L112
    1641           0 :     if( FD_UNLIKELY( ctx->instr->acct_cnt < 4 ) ) {
    1642           0 :       rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
    1643           0 :       break;
    1644           0 :     }
    1645             : 
    1646             :     // https://github.com/anza-xyz/agave/blob/v2.1.14/programs/vote/src/vote_processor.rs#L99-L100
    1647           0 :     fd_pubkey_t const * new_authority = NULL;
    1648           0 :     rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 3UL, &new_authority );
    1649           0 :     if( FD_UNLIKELY( rc ) ) return rc;
    1650             : 
    1651             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L116
    1652           0 :     if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( ctx->instr, 3, &rc ) ) ) {
    1653             :       /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
    1654           0 :       if( FD_UNLIKELY( !!rc ) ) break;
    1655             :       // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L117
    1656           0 :       rc = FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
    1657           0 :       break;
    1658           0 :     }
    1659             : 
    1660             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L119
    1661           0 :     rc = process_authorize_with_seed_instruction(
    1662           0 :         ctx,
    1663           0 :         target_version,
    1664           0 :         &me,
    1665           0 :         new_authority,
    1666           0 :         args->authorization_type,
    1667           0 :         &args->current_authority_derived_key_owner,
    1668           0 :         args->current_authority_derived_key_seed,
    1669           0 :         args->current_authority_derived_key_seed_len );
    1670             : 
    1671           0 :     break;
    1672           0 :   }
    1673             : 
    1674             :   /* UpdateValidatorIdentity
    1675             :    *
    1676             :    * Instruction:
    1677             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L65
    1678             :    *
    1679             :    * Processor:
    1680             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L130
    1681             :    */
    1682           0 :   case fd_vote_instruction_enum_update_validator_identity: {
    1683             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L131
    1684           0 :     if( FD_UNLIKELY( ctx->instr->acct_cnt < 2 ) ) {
    1685           0 :       rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
    1686           0 :       break;
    1687           0 :     }
    1688             : 
    1689             :     // https://github.com/anza-xyz/agave/blob/v2.1.14/programs/vote/src/vote_processor.rs#L118-L120
    1690           0 :     fd_pubkey_t const * node_pubkey = NULL;
    1691           0 :     rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 1UL, &node_pubkey );
    1692           0 :     if( FD_UNLIKELY( rc ) ) return rc;
    1693             : 
    1694             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L135
    1695           0 :     rc = update_validator_identity( ctx, target_version, &me, node_pubkey, signers );
    1696             : 
    1697           0 :     break;
    1698           0 :   }
    1699             : 
    1700             :   // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L142
    1701           0 :   case fd_vote_instruction_enum_update_commission: {
    1702             : 
    1703             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L149
    1704           0 :     fd_epoch_schedule_t epoch_schedule_;
    1705           0 :     fd_epoch_schedule_t const * epoch_schedule = fd_sysvar_cache_epoch_schedule_read( ctx->sysvar_cache, &epoch_schedule_ );
    1706           0 :     if( FD_UNLIKELY( !epoch_schedule ) ) {
    1707           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    1708           0 :     }
    1709             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L150
    1710             : 
    1711           0 :     fd_sol_sysvar_clock_t clock_;
    1712           0 :     fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
    1713           0 :     if( FD_UNLIKELY( !clock ) ) {
    1714           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    1715           0 :     }
    1716             : 
    1717             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L145
    1718           0 :     rc = update_commission(
    1719           0 :         ctx,
    1720           0 :         target_version,
    1721           0 :         &me,
    1722           0 :         instruction->inner.update_commission,
    1723           0 :         signers,
    1724           0 :         epoch_schedule,
    1725           0 :         clock
    1726           0 :     );
    1727             : 
    1728           0 :     break;
    1729           0 :   }
    1730             : 
    1731             :   /* Vote
    1732             :    *
    1733             :    * Instruction:
    1734             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L49
    1735             :    */
    1736           0 :   case fd_vote_instruction_enum_vote:;
    1737             :     /* clang-format off */
    1738           0 :     __attribute__((fallthrough));
    1739             :     /* clang-format on */
    1740             : 
    1741             :   /* VoteSwitch
    1742             :    *
    1743             :    * Instruction:
    1744             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L81
    1745             :    *
    1746             :    * Processor:
    1747             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L154
    1748             :    */
    1749           0 :   case fd_vote_instruction_enum_vote_switch: {
    1750           0 :     if( FD_FEATURE_ACTIVE_BANK( ctx->bank, deprecate_legacy_vote_ixs ) ) {
    1751           0 :       return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
    1752           0 :     }
    1753             : 
    1754           0 :     fd_vote_t * vote;
    1755           0 :     if( instruction->discriminant == fd_vote_instruction_enum_vote ) {
    1756           0 :       vote = &instruction->inner.vote;
    1757           0 :     } else if( instruction->discriminant == fd_vote_instruction_enum_vote_switch ) {
    1758           0 :       vote = &instruction->inner.vote_switch.vote;
    1759           0 :     } else {
    1760           0 :       FD_LOG_CRIT(( "unsupported instruction discriminant: %u", instruction->discriminant ));
    1761           0 :     }
    1762             : 
    1763             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L155
    1764           0 :     int err;
    1765           0 :     err = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_slot_hashes_id );
    1766           0 :     if( FD_UNLIKELY( err ) ) return err;
    1767             : 
    1768           0 :     if( FD_UNLIKELY( !fd_sysvar_cache_slot_hashes_is_valid( ctx->sysvar_cache ) ) ) {
    1769           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    1770           0 :     }
    1771             : 
    1772             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L157
    1773           0 :     err = fd_sysvar_instr_acct_check( ctx, 2, &fd_sysvar_clock_id );
    1774           0 :     if( FD_UNLIKELY( err ) ) return err;
    1775           0 :     fd_sol_sysvar_clock_t clock_;
    1776           0 :     fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
    1777           0 :     if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    1778             : 
    1779           0 :     fd_slot_hash_t const * slot_hashes = fd_sysvar_cache_slot_hashes_join_const( ctx->sysvar_cache ); /* guaranteed to succeed */
    1780           0 :     rc = process_vote_with_account(
    1781           0 :         ctx,
    1782           0 :         &me,
    1783           0 :         target_version,
    1784           0 :         slot_hashes,
    1785           0 :         clock,
    1786           0 :         vote,
    1787           0 :         signers
    1788           0 :     );
    1789           0 :     fd_sysvar_cache_slot_hashes_leave_const( ctx->sysvar_cache, slot_hashes );
    1790             : 
    1791           0 :     break;
    1792           0 :   }
    1793             : 
    1794             :   /* UpdateVoteState
    1795             :    *
    1796             :    * Instruction:
    1797             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L100
    1798             :    */
    1799           0 :   case fd_vote_instruction_enum_update_vote_state:;
    1800             :     /* clang-format off */
    1801           0 :     __attribute__((fallthrough));
    1802             :     /* clang-format on */
    1803             : 
    1804             :   /* UpdateVoteStateSwitch
    1805             :    *
    1806             :    * Instruction:
    1807             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L107
    1808             :    *
    1809             :    * Processor:
    1810             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L169
    1811             :    */
    1812           0 :   case fd_vote_instruction_enum_update_vote_state_switch: {
    1813           0 :     if( FD_FEATURE_ACTIVE_BANK( ctx->bank, deprecate_legacy_vote_ixs ) ) {
    1814           0 :       return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
    1815           0 :     }
    1816             : 
    1817           0 :     fd_vote_state_update_t * vote_state_update;
    1818           0 :     switch( instruction->discriminant ) {
    1819           0 :     case fd_vote_instruction_enum_update_vote_state:
    1820           0 :       vote_state_update = &instruction->inner.update_vote_state;
    1821           0 :       break;
    1822           0 :     case fd_vote_instruction_enum_update_vote_state_switch:
    1823           0 :       vote_state_update = &instruction->inner.update_vote_state_switch.vote_state_update;
    1824           0 :       break;
    1825           0 :     default:
    1826           0 :       FD_LOG_CRIT(( "unsupported instruction discriminant: %u", instruction->discriminant ));
    1827           0 :     }
    1828             : 
    1829             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L171
    1830           0 :     if( FD_LIKELY( !fd_sysvar_cache_slot_hashes_is_valid( ctx->sysvar_cache ) ) ) {
    1831           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    1832           0 :     }
    1833             : 
    1834             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L172
    1835           0 :     fd_sol_sysvar_clock_t clock_;
    1836           0 :     fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
    1837           0 :     if( FD_UNLIKELY( !clock ) )
    1838           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    1839             : 
    1840             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L173
    1841           0 :     fd_slot_hash_t const * slot_hashes = fd_sysvar_cache_slot_hashes_join_const( ctx->sysvar_cache );
    1842           0 :     rc = process_vote_state_update(
    1843           0 :         ctx,
    1844           0 :         &me,
    1845           0 :         target_version,
    1846           0 :         slot_hashes,
    1847           0 :         clock,
    1848           0 :         vote_state_update,
    1849           0 :         signers
    1850           0 :     );
    1851           0 :     fd_sysvar_cache_slot_hashes_leave_const( ctx->sysvar_cache, slot_hashes );
    1852             : 
    1853           0 :     break;
    1854           0 :   }
    1855             : 
    1856             :   /* CompactUpdateVoteState
    1857             :    *
    1858             :    * Instruction:
    1859             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L139
    1860             :    *
    1861             :    * Notes:
    1862             :    * - Up to three signers: the vote authority, the authorized withdrawer, and the new authority.
    1863             :    * - Feature gated, but live on mainnet.
    1864             :    */
    1865           0 :   case fd_vote_instruction_enum_compact_update_vote_state:;
    1866           0 :     __attribute__((fallthrough));
    1867             : 
    1868             :   /* CompactUpdateVoteStateSwitch
    1869             :    *
    1870             :    * Instruction:
    1871             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L146
    1872             :    *
    1873             :    * Processor:
    1874             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L183
    1875             :    *
    1876             :    * Notes:
    1877             :    * - Up to three signers: the vote authority, the authorized withdrawer, and the new authority.
    1878             :    * - Feature gated, but live on mainnet.
    1879             :    */
    1880           0 :   case fd_vote_instruction_enum_compact_update_vote_state_switch: {
    1881             :     /* https://github.com/anza-xyz/agave/blob/dc4b9dcbbf859ff48f40d00db824bde063fdafcc/programs/vote/src/vote_processor.rs#L183-L191 */
    1882           0 :     if( FD_FEATURE_ACTIVE_BANK( ctx->bank, deprecate_legacy_vote_ixs ) ) {
    1883           0 :       return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
    1884           0 :     }
    1885             : 
    1886           0 :     fd_compact_vote_state_update_t * vote_state_update = NULL;
    1887           0 :     if( instruction->discriminant == fd_vote_instruction_enum_compact_update_vote_state ) {
    1888           0 :       vote_state_update = &instruction->inner.compact_update_vote_state;
    1889           0 :     } else if( instruction->discriminant ==
    1890           0 :                fd_vote_instruction_enum_compact_update_vote_state_switch ) {
    1891           0 :       vote_state_update =
    1892           0 :           &instruction->inner.compact_update_vote_state_switch.compact_vote_state_update;
    1893           0 :     }
    1894             : 
    1895           0 :     fd_vote_state_update_t vote_update;
    1896           0 :     fd_vote_state_update_new( &vote_update );
    1897           0 :     if( FD_UNLIKELY( !fd_vote_decode_compact_update( vote_state_update, &vote_update, ctx ) ) )
    1898           0 :       return FD_EXECUTOR_INSTR_ERR_INVALID_INSTR_DATA;
    1899             : 
    1900             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L185
    1901           0 :     if( FD_LIKELY( !fd_sysvar_cache_slot_hashes_is_valid( ctx->sysvar_cache ) ) ) {
    1902           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    1903           0 :     }
    1904             : 
    1905           0 :     fd_sol_sysvar_clock_t clock_;
    1906           0 :     fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
    1907           0 :     if( FD_UNLIKELY( !clock ) )
    1908           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    1909             : 
    1910             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L187
    1911           0 :     fd_slot_hash_t const * slot_hashes = fd_sysvar_cache_slot_hashes_join_const( ctx->sysvar_cache ); /* guaranteed to succeed */
    1912           0 :     rc = process_vote_state_update(
    1913           0 :         ctx,
    1914           0 :         &me,
    1915           0 :         target_version,
    1916           0 :         slot_hashes,
    1917           0 :         clock,
    1918           0 :         &vote_update,
    1919           0 :         signers
    1920           0 :     );
    1921           0 :     fd_sysvar_cache_slot_hashes_leave_const( ctx->sysvar_cache, slot_hashes );
    1922             : 
    1923           0 :     break;
    1924           0 :   }
    1925             : 
    1926             :   /* TowerSync(Switch)
    1927             :    *
    1928             :    * Instruction:
    1929             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L151-L157
    1930             :    *
    1931             :    * Processor:
    1932             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L196-L215
    1933             :    */
    1934             : 
    1935           0 :   case fd_vote_instruction_enum_tower_sync:
    1936           0 :   case fd_vote_instruction_enum_tower_sync_switch: {
    1937           0 :     fd_tower_sync_t * tower_sync = (instruction->discriminant == fd_vote_instruction_enum_tower_sync)
    1938           0 :         ? &instruction->inner.tower_sync
    1939           0 :         : &instruction->inner.tower_sync_switch.tower_sync;
    1940             : 
    1941           0 :     if( FD_LIKELY( !fd_sysvar_cache_slot_hashes_is_valid( ctx->sysvar_cache ) ) ) {
    1942           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    1943           0 :     }
    1944             : 
    1945           0 :     fd_sol_sysvar_clock_t clock_;
    1946           0 :     fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
    1947           0 :     if( FD_UNLIKELY( !clock ) ) {
    1948           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    1949           0 :     }
    1950             : 
    1951           0 :     fd_slot_hash_t const * slot_hashes = fd_sysvar_cache_slot_hashes_join_const( ctx->sysvar_cache );
    1952           0 :     FD_TEST( slot_hashes );
    1953           0 :     rc = process_tower_sync(
    1954           0 :         ctx,
    1955           0 :         &me,
    1956           0 :         target_version,
    1957           0 :         slot_hashes,
    1958           0 :         clock,
    1959           0 :         tower_sync,
    1960           0 :         signers
    1961           0 :     );
    1962           0 :     fd_sysvar_cache_slot_hashes_leave_const( ctx->sysvar_cache, slot_hashes );
    1963             : 
    1964           0 :     break;
    1965           0 :   }
    1966             : 
    1967             :   /* Withdraw
    1968             :    *
    1969             :    * Instruction:
    1970             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L57
    1971             :    *
    1972             :    * Processor:
    1973             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L216
    1974             :    */
    1975           0 :   case fd_vote_instruction_enum_withdraw: {
    1976           0 :     if( FD_UNLIKELY( ctx->instr->acct_cnt < 2 ) ) {
    1977           0 :       rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
    1978           0 :       break;
    1979           0 :     }
    1980           0 :     fd_rent_t rent_;
    1981           0 :     fd_rent_t const * rent_sysvar = fd_sysvar_cache_rent_read( ctx->sysvar_cache, &rent_ );
    1982           0 :     if( FD_UNLIKELY( !rent_sysvar ) )
    1983           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    1984           0 :     fd_sol_sysvar_clock_t clock_;
    1985           0 :     fd_sol_sysvar_clock_t const * clock_sysvar = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
    1986           0 :     if( FD_UNLIKELY( !clock_sysvar ) )
    1987           0 :       return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    1988             : 
    1989           0 :     rc = withdraw(
    1990           0 :         ctx,
    1991           0 :         &me,
    1992           0 :         target_version,
    1993           0 :         instruction->inner.withdraw,
    1994           0 :         1UL,
    1995           0 :         signers,
    1996           0 :         rent_sysvar,
    1997           0 :         clock_sysvar
    1998           0 :     );
    1999             : 
    2000           0 :     break;
    2001           0 :   }
    2002             : 
    2003             :   /* AuthorizeChecked
    2004             :    *
    2005             :    * Instruction:
    2006             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/sdk/program/src/vote/instruction.rs#L93
    2007             :    *
    2008             :    * Processor:
    2009             :    * https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L234
    2010             :    *
    2011             :    * Notes:
    2012             :    * - Up to three signers: the vote authority, the authorized withdrawer, and the new authority.
    2013             :    * - Feature gated, but live on mainnet.
    2014             :    */
    2015           0 :   case fd_vote_instruction_enum_authorize_checked: {
    2016           0 :     if( FD_UNLIKELY( ctx->instr->acct_cnt < 4 ) ) {
    2017           0 :       rc = FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
    2018           0 :       break;
    2019           0 :     }
    2020             : 
    2021             :     // https://github.com/anza-xyz/agave/blob/v2.1.14/programs/vote/src/vote_processor.rs#L243-L245
    2022           0 :     fd_pubkey_t const * voter_pubkey = NULL;
    2023           0 :     rc = fd_exec_instr_ctx_get_key_of_account_at_index( ctx, 3UL, &voter_pubkey );
    2024           0 :     if( FD_UNLIKELY( rc ) ) return rc;
    2025             : 
    2026             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L239
    2027           0 :     if( FD_UNLIKELY( !fd_instr_acc_is_signer_idx( ctx->instr, 3, &rc ) ) ) {
    2028             :       /* https://github.com/anza-xyz/agave/blob/v3.0.3/transaction-context/src/lib.rs#L789 */
    2029           0 :       if( FD_UNLIKELY( !!rc ) ) break;
    2030             : 
    2031           0 :       rc = FD_EXECUTOR_INSTR_ERR_MISSING_REQUIRED_SIGNATURE;
    2032           0 :       break;
    2033           0 :     }
    2034             : 
    2035             :     // https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L242
    2036           0 :     rc = fd_sysvar_instr_acct_check( ctx, 1, &fd_sysvar_clock_id );
    2037           0 :     if( FD_UNLIKELY( rc ) ) return rc;
    2038           0 :     fd_sol_sysvar_clock_t clock_;
    2039           0 :     fd_sol_sysvar_clock_t const * clock = fd_sysvar_cache_clock_read( ctx->sysvar_cache, &clock_ );
    2040           0 :     if( FD_UNLIKELY( !clock ) ) return FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
    2041             : 
    2042           0 :     rc = authorize(
    2043           0 :         ctx,
    2044           0 :         &me,
    2045           0 :         target_version,
    2046           0 :         voter_pubkey,
    2047           0 :         instruction->inner.authorize_checked,
    2048           0 :         signers,
    2049           0 :         clock
    2050           0 :     );
    2051           0 :     break;
    2052           0 :   }
    2053             : 
    2054           0 :   default:
    2055           0 :     FD_LOG_CRIT(( "unsupported vote instruction: %u", instruction->discriminant ));
    2056           0 :   }
    2057             : 
    2058           0 :   return rc;
    2059           0 : }
    2060             : 
    2061             : /**********************************************************************/
    2062             : /* Public API                                                         */
    2063             : /**********************************************************************/
    2064             : 
    2065             : /* TODO: Old code, remove whenever stake program gets cleaned up */
    2066             : void
    2067             : fd_vote_convert_to_current( fd_vote_state_versioned_t * self,
    2068             :                             uchar *                     authorized_voters_mem,
    2069           0 :                             uchar *                     landed_votes_mem ) {
    2070           0 :   fd_vsv_try_convert_to_v3( self, authorized_voters_mem, landed_votes_mem );
    2071           0 : }
    2072             : 
    2073             : fd_vote_state_versioned_t *
    2074             : fd_vote_get_state( fd_account_meta_t const * self,
    2075           0 :                    uchar *                   mem ) {
    2076           0 :   int err = fd_vsv_get_state( self, mem );
    2077           0 :   return err ? NULL : (fd_vote_state_versioned_t *)mem;
    2078           0 : }

Generated by: LCOV version 1.14