LCOV - code coverage report
Current view: top level - flamenco/runtime/program/vote - fd_vote_codec.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 728 1033 70.5 %
Date: 2026-11-11 12:40:43 Functions: 37 44 84.1 %

          Line data    Source code
       1             : #include "fd_vote_codec_tmpl.h"
       2             : #include "../../../../ballet/utf8/fd_utf8.h"
       3             : #include "../../../../ballet/txn/fd_compact_u16.h"
       4             : #include "../../../../ballet/txn/fd_txn.h"
       5             : 
       6             : /**********************************************************************/
       7             : /* Unified bincode (de)serialization macros.                          */
       8             : /* All macros operate on a cursor (uchar const ** data, ulong * sz).  */
       9             : /* CHECK returns 1 on failure (non-zero == error).                    */
      10             : /**********************************************************************/
      11             : 
      12      731364 : #define CHECK( cond ) do {               \
      13      731364 :   if( FD_UNLIKELY( !(cond) ) ) return 1; \
      14      731364 : } while( 0 )
      15             : 
      16        9390 : #define CHECK_RET_NULL( cond ) do {          \
      17        9390 :   if( FD_UNLIKELY( !(cond) ) ) return NULL;  \
      18        9390 : } while( 0 )
      19             : 
      20         303 : #define CHECK_U64_MUL_OVERFLOW( a, b ) do {                \
      21         303 :   ulong _dummy;                                            \
      22         303 :   CHECK( !__builtin_umull_overflow( (a), (b), &_dummy ) ); \
      23         303 : } while( 0 )
      24             : 
      25             : /**********************************************************************/
      26             : /* Read macros                                                        */
      27             : /**********************************************************************/
      28             : 
      29      102999 : #define READ_BYTES( dst, n, data, sz ) do { \
      30      102999 :   CHECK( (n)<=(*(sz)) );                    \
      31      102999 :   fd_memcpy( (dst), *(data), (n) );         \
      32      102791 :   *(data) += (n);                           \
      33      102791 :   *(sz) -= (n);                             \
      34      102791 : } while( 0 )
      35             : 
      36         434 : #define SKIP_BYTES( n, data, sz ) do { \
      37         434 :   CHECK( (n)<=(*(sz)) );               \
      38         434 :   *(data) += (n);                      \
      39         434 :   *(sz) -= (n);                        \
      40         434 : } while( 0 )
      41             : 
      42       81777 : #define READ_U8( dst, data, sz ) do { \
      43       81777 :   CHECK( 1UL<=(*(sz)) );              \
      44       81777 :   (dst) = FD_LOAD( uchar, *(data) );  \
      45       81750 :   *(data) += 1UL;                     \
      46       81750 :   *(sz) -= 1UL;                       \
      47       81750 : } while( 0 )
      48             : 
      49        2516 : #define READ_U16( dst, data, sz ) do {  \
      50        2516 :   CHECK( 2UL<=(*(sz)) );                \
      51        2516 :   (dst) = FD_LOAD( ushort, *(data) );   \
      52        2516 :   *(data) += 2UL;                       \
      53        2516 :   *(sz) -= 2UL;                         \
      54        2516 : } while( 0 )
      55             : 
      56       86321 : #define READ_U32( dst, data, sz ) do { \
      57       86321 :   CHECK( 4UL<=(*(sz)) );               \
      58       86321 :   (dst) = FD_LOAD( uint, *(data) );    \
      59       86234 :   *(data) += 4UL;                      \
      60       86234 :   *(sz) -= 4UL;                        \
      61       86234 : } while( 0 )
      62             : 
      63           0 : #define READ_I32( dst, data, sz ) do { \
      64           0 :   CHECK( 4UL<=(*(sz)) );               \
      65           0 :   (dst) = FD_LOAD( int, *(data) );     \
      66           0 :   *(data) += 4UL;                      \
      67           0 :   *(sz) -= 4UL;                        \
      68           0 : } while( 0 )
      69             : 
      70      124639 : #define READ_U64( dst, data, sz ) do {  \
      71      124639 :   CHECK( 8UL<=(*(sz)) );                \
      72      124639 :   (dst) = FD_LOAD( ulong, *(data) );    \
      73      124580 :   *(data) += 8UL;                       \
      74      124580 :   *(sz) -= 8UL;                         \
      75      124580 : } while( 0 )
      76             : 
      77        1170 : #define READ_I64( dst, data, sz ) do { \
      78        1170 :   CHECK( 8UL<=(*(sz)) );               \
      79        1170 :   (dst) = FD_LOAD( long, *(data) );    \
      80        1159 :   *(data) += 8UL;                      \
      81        1159 :   *(sz) -= 8UL;                        \
      82        1159 : } while( 0 )
      83             : 
      84             : #define READ_HASH( dst, data, sz ) \
      85       16226 :   READ_BYTES( (dst).uc, 32UL, data, sz )
      86             : 
      87       12361 : #define READ_PUBKEY READ_HASH
      88             : 
      89        8677 : #define READ_BOOL( dst, data, sz ) do { \
      90        8677 :   READ_U8( dst, data, sz );             \
      91        8677 :   CHECK( (dst)==0 || (dst)==1 );        \
      92        8662 : } while( 0 )
      93             : 
      94        7243 : #define READ_OPTION READ_BOOL
      95             : 
      96        1127 : #define READ_ENUM( dst, n, data, sz ) do { \
      97        1127 :   CHECK( 4UL<=(*(sz)) );                   \
      98        1127 :   (dst) = FD_LOAD( uint, *(data) );        \
      99        1125 :   CHECK( (dst)<(n) );                      \
     100        1125 :   *(data) += 4UL;                          \
     101        1117 :   *(sz) -= 4UL;                            \
     102        1117 : } while( 0 )
     103             : 
     104             : /* Varint decoder for u64 (LEB128). */
     105       31917 : #define READ_U64_VARINT( dst, data, sz ) do {                                  \
     106       31917 :   ulong _val = 0UL;                                                            \
     107       31917 :   uint  _shift = 0U;                                                           \
     108       57974 :   for(;;) {                                                                    \
     109       57974 :     CHECK( 1UL<=(*(sz)) );                                                     \
     110       57974 :     uchar _byte = FD_LOAD( uchar, *(data) );                                   \
     111       57955 :     *(data) += 1UL;                                                            \
     112       57955 :     *(sz) -= 1UL;                                                              \
     113       57955 :     _val |= (ulong)(_byte & 0x7F) << _shift;                                   \
     114       57955 :     if( FD_LIKELY( !(_byte & 0x80) ) ) {                                       \
     115       31898 :       CHECK( (_val>>_shift)==(ulong)_byte );     /* last byte not truncated */ \
     116       31898 :       CHECK( _byte || !_shift );                 /* no trailing zero bytes */  \
     117       31898 :       (dst) = _val;                                                            \
     118       31891 :       break;                                                                   \
     119       31898 :     }                                                                          \
     120       57955 :     _shift += 7U;                                                              \
     121       26057 :     CHECK( _shift<64U );                                                       \
     122       26057 :   }                                                                            \
     123       31917 : } while( 0 )
     124             : 
     125        1085 : #define READ_COMPACT_U16( dst, data, sz ) do {      \
     126        1085 :   ulong _n = fd_cu16_dec( *(data), *(sz), &(dst) ); \
     127        1085 :   CHECK( _n );                                      \
     128        1085 :   *(data) += _n;                                    \
     129        1012 :   *(sz)   -= _n;                                    \
     130        1012 : } while( 0 )
     131             : 
     132             : /**********************************************************************/
     133             : /* Write macros                                                       */
     134             : /**********************************************************************/
     135             : 
     136       36372 : #define WRITE_BYTES( src, n, out, out_sz ) do { \
     137       36372 :   CHECK( (n)<=(*(out_sz)) );                    \
     138       36372 :   fd_memcpy( *(out), (src), (n) );              \
     139       36372 :   *(out) += (n);                                \
     140       36372 :   *(out_sz) -= (n);                             \
     141       36372 : } while( 0 )
     142             : 
     143       17396 : #define WRITE_U8( val, out, out_sz ) do { \
     144       17396 :   CHECK( 1UL<=(*(out_sz)) );              \
     145       17396 :   FD_STORE( uchar, *(out), (val) );       \
     146       17396 :   *(out) += 1UL;                          \
     147       17396 :   *(out_sz) -= 1UL;                       \
     148       17396 : } while( 0 )
     149             : 
     150        2172 : #define WRITE_U16( val, out, out_sz ) do { \
     151        2172 :   CHECK( 2UL<=(*(out_sz)) );               \
     152        2172 :   FD_STORE( ushort, *(out), (val) );       \
     153        2172 :   *(out) += 2UL;                           \
     154        2172 :   *(out_sz) -= 2UL;                        \
     155        2172 : } while( 0 )
     156             : 
     157       16314 : #define WRITE_U32( val, out, out_sz ) do { \
     158       16314 :   CHECK( 4UL<=(*(out_sz)) );               \
     159       16314 :   FD_STORE( uint, *(out), (val) );         \
     160       16314 :   *(out) += 4UL;                           \
     161       16314 :   *(out_sz) -= 4UL;                        \
     162       16314 : } while( 0 )
     163             : 
     164       21301 : #define WRITE_U64( val, out, out_sz ) do { \
     165       21301 :   CHECK( 8UL<=(*(out_sz)) );               \
     166       21301 :   FD_STORE( ulong, *(out), (val) );        \
     167       21301 :   *(out) += 8UL;                           \
     168       21301 :   *(out_sz) -= 8UL;                        \
     169       21301 : } while( 0 )
     170             : 
     171             : #define WRITE_I64( val, out, out_sz ) do { \
     172             :   CHECK( 8UL<=(*(out_sz)) );               \
     173             :   FD_STORE( long, *(out), (val) );         \
     174             :   *(out) += 8UL;                           \
     175             :   *(out_sz) -= 8UL;                        \
     176             : } while( 0 )
     177             : 
     178             : #define WRITE_PUBKEY( src, out, out_sz ) \
     179        5545 :   WRITE_BYTES( (src).uc, 32UL, out, out_sz )
     180             : 
     181             : #define WRITE_BOOL( val, out, out_sz ) \
     182        2172 :   WRITE_U8( (uchar)!!(val), out, out_sz )
     183             : 
     184        1086 : #define WRITE_OPTION WRITE_BOOL
     185             : 
     186             : /**********************************************************************/
     187             : /* Vote account state -- deserialization helpers                       */
     188             : /* Each returns 0 on success, 1 on failure.                           */
     189             : /**********************************************************************/
     190             : 
     191             : /* Votes (is_v1_14_11=1 for v1_14_11 Vec<Lockout>, 0 for v3/v4
     192             :    Vec<LandedVote>) */
     193             : static int
     194             : deser_votes( fd_landed_vote_t * votes,
     195             :              int                is_v1_14_11,
     196             :              uchar const **     ptr,
     197        2948 :              ulong *            rem ) {
     198        2948 :   ulong votes_len;
     199        2948 :   READ_U64( votes_len, ptr, rem );
     200        2943 :   CHECK( votes_len<=MAX_LOCKOUT_HISTORY );
     201             : 
     202       44934 :   for( ulong i=0UL; i<votes_len; i++ ) {
     203       42037 :     fd_landed_vote_t * elem = deq_fd_landed_vote_t_push_tail_nocopy( votes );
     204       42037 :     if( is_v1_14_11 ) {
     205        3223 :       elem->latency = 0; /* Unused field for v1_14_11 */
     206       38814 :     } else {
     207       38814 :       READ_U8( elem->latency, ptr, rem );
     208       38814 :     }
     209       42037 :     READ_U64( elem->lockout.slot, ptr, rem );
     210       42037 :     READ_U32( elem->lockout.confirmation_count, ptr, rem );
     211       42037 :   }
     212        2897 :   return 0;
     213        2897 : }
     214             : 
     215             : /* Root slot (Option<u64>) */
     216             : static int
     217             : deser_root_slot( uchar *         has_root_slot,
     218             :                  ulong *         root_slot,
     219             :                  uchar const **  ptr,
     220        2896 :                  ulong *         rem ) {
     221        2896 :   uchar opt;
     222        2896 :   READ_OPTION( opt, ptr, rem );
     223        2884 :   *has_root_slot = opt;
     224        2884 :   if( opt ) {
     225        1374 :     READ_U64( *root_slot, ptr, rem );
     226        1374 :   }
     227        2879 :   return 0;
     228        2884 : }
     229             : 
     230             : /* Authorized voters (BTreeMap<u64, Pubkey>) */
     231             : static int
     232             : deser_authorized_voters( fd_vote_authorized_voter_t *        pool,
     233             :                          fd_vote_authorized_voters_treap_t * treap,
     234             :                          uchar const **                      ptr,
     235        2880 :                          ulong *                             rem ) {
     236        2880 :   ulong authorized_voters_len;
     237        2880 :   READ_U64( authorized_voters_len, ptr, rem );
     238        2874 :   CHECK( authorized_voters_len<=MAX_AUTHORIZED_VOTERS );
     239        5602 :   for( ulong i=0UL; i<authorized_voters_len; i++ ) {
     240        2865 :     fd_vote_authorized_voter_t * voter = fd_vote_authorized_voters_pool_ele_acquire( pool );
     241        2865 :     READ_U64( voter->epoch, ptr, rem );
     242        2865 :     READ_PUBKEY( voter->pubkey, ptr, rem );
     243        2865 :     voter->prio = voter->pubkey.uc[0];
     244             : 
     245             :     /* Check for existing entries, overwrite if exists */
     246        2865 :     fd_vote_authorized_voter_t * existing_voter = fd_vote_authorized_voters_treap_ele_query( treap, voter->epoch, pool );
     247        2865 :     if( FD_UNLIKELY( existing_voter ) ) {
     248           2 :       fd_vote_authorized_voters_treap_ele_remove( treap, existing_voter, pool );
     249           2 :       fd_vote_authorized_voters_pool_ele_release( pool, existing_voter );
     250           2 :     }
     251             : 
     252        2865 :     fd_vote_authorized_voters_treap_ele_insert( treap, voter, pool );
     253        2865 :   }
     254        2737 :   return 0;
     255        2737 : }
     256             : 
     257             : /* Prior voters (fixed-size circular buffer of 32 entries)
     258             :    We can directly memcpy the entire buffer because the wire
     259             :    format is identical to the in-memory representation of the
     260             :    array. */
     261             : static int
     262             : deser_prior_voters( fd_vote_prior_voters_t * prior_voters,
     263             :                     uchar const **           ptr,
     264        1483 :                     ulong *                  rem ) {
     265        1483 :   READ_BYTES( prior_voters->buf, PRIOR_VOTERS_MAX*sizeof(fd_vote_prior_voter_t), ptr, rem );
     266        1434 :   READ_U64( prior_voters->idx, ptr, rem );
     267        1434 :   READ_BOOL( prior_voters->is_empty, ptr, rem );
     268        1429 :   return 0;
     269        1434 : }
     270             : 
     271             : /* Epoch credits (Vec<EpochCredits>) */
     272             : static int
     273             : deser_epoch_credits( fd_vote_epoch_credits_t * epoch_credits,
     274             :                      uchar const **            ptr,
     275        2684 :                      ulong *                   rem ) {
     276        2684 :   ulong epoch_credits_len;
     277        2684 :   READ_U64( epoch_credits_len, ptr, rem );
     278        2682 :   CHECK( epoch_credits_len<=MAX_EPOCH_CREDITS_HISTORY );
     279       83827 :   for( ulong i=0UL; i<epoch_credits_len; i++ ) {
     280       81151 :     fd_vote_epoch_credits_t * elem = deq_fd_vote_epoch_credits_t_push_tail_nocopy( epoch_credits );
     281       81151 :     READ_BYTES( elem, sizeof(fd_vote_epoch_credits_t), ptr, rem );
     282       81151 :   }
     283        2676 :   return 0;
     284        2678 : }
     285             : 
     286             : /**********************************************************************/
     287             : /* Vote account state -- serialization helpers                        */
     288             : /* Each returns 0 on success, 1 on failure.                           */
     289             : /**********************************************************************/
     290             : 
     291             : /* Votes (is_v1_14_11=1 for v1_14_11 Vec<Lockout>, 0 for v3/v4
     292             :    Vec<LandedVote>) */
     293             : static int
     294             : ser_votes( fd_landed_vote_t const * votes,
     295             :            int                      is_v1_14_11,
     296             :            uchar **                 out,
     297        1086 :            ulong *                  out_sz ) {
     298        1086 :   if( FD_UNLIKELY( votes==NULL ) ) {
     299           0 :     WRITE_U64( 0UL, out, out_sz );
     300           0 :     return 0;
     301           0 :   }
     302             : 
     303        1086 :   ulong votes_len = deq_fd_landed_vote_t_cnt( votes );
     304        1086 :   WRITE_U64( votes_len, out, out_sz );
     305             : 
     306        1086 :   for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( votes );
     307       16314 :        !deq_fd_landed_vote_t_iter_done( votes, iter );
     308       15228 :        iter = deq_fd_landed_vote_t_iter_next( votes, iter ) ) {
     309       15228 :     fd_landed_vote_t const * elem = deq_fd_landed_vote_t_iter_ele_const( votes, iter );
     310       15228 :     if( !is_v1_14_11 ) {
     311       15224 :       WRITE_U8( elem->latency, out, out_sz );
     312       15224 :     }
     313       15228 :     WRITE_U64( elem->lockout.slot, out, out_sz );
     314       15228 :     WRITE_U32( elem->lockout.confirmation_count, out, out_sz );
     315       15228 :   }
     316        1086 :   return 0;
     317        1086 : }
     318             : 
     319             : /* Root slot (Option<u64>) */
     320             : static int
     321             : ser_root_slot( uchar    has_root_slot,
     322             :                ulong    root_slot,
     323             :                uchar ** out,
     324        1086 :                ulong *  out_sz ) {
     325        1086 :   WRITE_BOOL( has_root_slot, out, out_sz );
     326        1086 :   if( has_root_slot ) {
     327         528 :     WRITE_U64( root_slot, out, out_sz );
     328         528 :   }
     329        1086 :   return 0;
     330        1086 : }
     331             : 
     332             : /* Authorized voters (BTreeMap<u64, Pubkey>) */
     333             : static int
     334             : ser_authorized_voters( fd_vote_authorized_voter_t const *        pool,
     335             :                        fd_vote_authorized_voters_treap_t const * treap,
     336             :                        uchar **                                  out,
     337        1086 :                        ulong *                                   out_sz ) {
     338        1086 :   if( FD_UNLIKELY( treap==NULL ) ) {
     339           0 :     WRITE_U64( 0UL, out, out_sz );
     340           0 :     return 0;
     341           0 :   }
     342             : 
     343        1086 :   ulong len = fd_vote_authorized_voters_treap_ele_cnt( treap );
     344        1086 :   WRITE_U64( len, out, out_sz );
     345             : 
     346        1086 :   for( fd_vote_authorized_voters_treap_fwd_iter_t iter = fd_vote_authorized_voters_treap_fwd_iter_init( treap, pool );
     347        2287 :        !fd_vote_authorized_voters_treap_fwd_iter_done( iter );
     348        1201 :        iter = fd_vote_authorized_voters_treap_fwd_iter_next( iter, pool ) ) {
     349        1201 :     fd_vote_authorized_voter_t const * voter = fd_vote_authorized_voters_treap_fwd_iter_ele_const( iter, pool );
     350        1201 :     WRITE_U64( voter->epoch, out, out_sz );
     351        1201 :     WRITE_PUBKEY( voter->pubkey, out, out_sz );
     352        1201 :   }
     353        1086 :   return 0;
     354        1086 : }
     355             : 
     356             : /* Prior voters (fixed-size circular buffer of 32 entries)
     357             :    We can directly memcpy the entire buffer because the wire
     358             :    format is identical to the in-memory representation of the
     359             :    array. */
     360             : static int
     361             : ser_prior_voters( fd_vote_prior_voters_t const * prior_voters,
     362             :                   uchar **                       out,
     363           0 :                   ulong *                        out_sz ) {
     364           0 :   WRITE_BYTES( prior_voters->buf, PRIOR_VOTERS_MAX*sizeof(fd_vote_prior_voter_t), out, out_sz );
     365           0 :   WRITE_U64( prior_voters->idx, out, out_sz );
     366           0 :   WRITE_BOOL( prior_voters->is_empty, out, out_sz );
     367           0 :   return 0;
     368           0 : }
     369             : 
     370             : /* Epoch credits (Vec<EpochCredits>) */
     371             : static int
     372             : ser_epoch_credits( fd_vote_epoch_credits_t const * epoch_credits,
     373             :                    uchar **                        out,
     374        1086 :                    ulong *                         out_sz ) {
     375        1086 :   if( FD_UNLIKELY( epoch_credits==NULL ) ) {
     376           0 :     WRITE_U64( 0UL, out, out_sz );
     377           0 :     return 0;
     378           0 :   }
     379             : 
     380        1086 :   ulong len = deq_fd_vote_epoch_credits_t_cnt( epoch_credits );
     381        1086 :   WRITE_U64( len, out, out_sz );
     382             : 
     383        1086 :   for( deq_fd_vote_epoch_credits_t_iter_t iter = deq_fd_vote_epoch_credits_t_iter_init( epoch_credits );
     384       30724 :        !deq_fd_vote_epoch_credits_t_iter_done( epoch_credits, iter );
     385       29638 :        iter = deq_fd_vote_epoch_credits_t_iter_next( epoch_credits, iter ) ) {
     386       29638 :     fd_vote_epoch_credits_t const * elem = deq_fd_vote_epoch_credits_t_iter_ele_const( epoch_credits, iter );
     387       29638 :     WRITE_BYTES( elem, sizeof(fd_vote_epoch_credits_t), out, out_sz );
     388       29638 :   }
     389        1086 :   return 0;
     390        1086 : }
     391             : 
     392             : /**********************************************************************/
     393             : /* Wire layout offsets                                                */
     394             : /**********************************************************************/
     395             : 
     396             : /* Fixed-offset byte positions within the bincode wire format.
     397             :    For v1_14_11 and v3 the fixed prefix is: discriminant, node_pubkey,
     398             :    authorized_withdrawer, commission.  For v4 the prefix additionally
     399             :    includes inflation_rewards_collector, block_revenue_collector,
     400             :    inflation_rewards_commission_bps, block_revenue_commission_bps,
     401             :    and pending_delegator_rewards before the first variable field. */
     402             : 
     403         101 : #define WIRE_OFF_NODE_PUBKEY                     (4UL)
     404           0 : #define WIRE_OFF_V1V3_COMMISSION                 (68UL)  /* 4 + 32 + 32 */
     405         101 : #define WIRE_OFF_V4_INFLATION_REWARDS_COLLECTOR  (68UL)  /* 4 + 32 + 32 */
     406         101 : #define WIRE_OFF_V4_BLOCK_REVENUE_COLLECTOR      (100UL) /* 4 + 32 + 32 + 32 */
     407             : #define WIRE_OFF_V4_COMMISSION_BPS               (132UL) /* 4 + 32 + 32 + 32 + 32 */
     408             : #define WIRE_OFF_V4_BLOCK_REVENUE_COMMISSION_BPS (134UL) /* 4 + 32 + 32 + 32 + 32 + 2 */
     409           0 : #define WIRE_OFF_V4_PENDING_DELEGATOR_REWARDS    (136UL) /* 4 + 32 + 32 + 32 + 32 + 2 + 2 */
     410             : 
     411             : /* Byte size of a Lockout on the wire: u64 slot + u32 confirmation_count */
     412           0 : #define WIRE_LOCKOUT_SZ     (12UL)
     413             : /* Byte size of a LandedVote on the wire: u8 latency + u64 slot + u32 confirmation_count */
     414        1086 : #define WIRE_LANDED_VOTE_SZ (13UL)
     415             : /* Byte size of an authorized voter entry: u64 epoch + 32B pubkey */
     416        1086 : #define WIRE_AUTH_VOTER_SZ  (40UL)
     417             : /* Byte size of prior_voters on the wire: 32 * 48B + 8B idx + 1B is_empty */
     418           0 : #define WIRE_PRIOR_VOTERS_SZ (PRIOR_VOTERS_MAX * 48UL + 8UL + 1UL)
     419             : 
     420             : /* Offset of the votes vector (v1/v3) or BLS option (v4) — first byte
     421             :    after the fixed-offset prefix. */
     422           0 : #define WIRE_VOTES_OFF_V1_V3 (69UL)   /* 4 + 32 + 32 + 1 */
     423        1493 : #define WIRE_BLS_OFF_V4      (144UL)  /* 4 + 32 + 32 + 32 + 32 + 2 + 2 + 8 */
     424             : 
     425             : /* Offset of a Vote State V5 field */
     426           0 : #define V5_OFF( f ) (4UL+offsetof( fd_vote_state_v5_t, f ))
     427             : 
     428             : FD_STATIC_ASSERT( V5_OFF( node_pubkey                      )==WIRE_OFF_NODE_PUBKEY,                     v5_layout );
     429             : FD_STATIC_ASSERT( V5_OFF( inflation_rewards_collector      )==WIRE_OFF_V4_INFLATION_REWARDS_COLLECTOR,  v5_layout );
     430             : FD_STATIC_ASSERT( V5_OFF( block_revenue_collector          )==WIRE_OFF_V4_BLOCK_REVENUE_COLLECTOR,      v5_layout );
     431             : FD_STATIC_ASSERT( sizeof(fd_vote_state_v5_authorized_voter_t)==WIRE_AUTH_VOTER_SZ,                      v5_layout );
     432             : 
     433             : static void
     434             : v5_commission_load( uchar const *                   data,
     435             :                     uint                            kind,
     436           0 :                     fd_vote_state_v5_commission_t * out ) {
     437           0 :   int   block      = kind==fd_commission_kind_enum_block_revenue;
     438           0 :   ulong len_off    = block ? V5_OFF( block_revenue_commission_bps_len    ) : V5_OFF( inflation_rewards_commission_bps_len    );
     439           0 :   ulong epochs_off = block ? V5_OFF( block_revenue_commission_bps_epochs ) : V5_OFF( inflation_rewards_commission_bps_epochs );
     440           0 :   ulong bps_off    = block ? V5_OFF( block_revenue_commission_bps        ) : V5_OFF( inflation_rewards_commission_bps        );
     441           0 :   out->len = FD_LOAD( uint, data+len_off );
     442           0 :   for( ulong i=0UL; i<FD_VOTE_STATE_V5_COMMISSION_MAX; i++ ) {
     443           0 :     out->epoch[ i ] = FD_LOAD( ulong,  data+epochs_off+i*sizeof(ulong)  );
     444           0 :     out->bps  [ i ] = FD_LOAD( ushort, data+bps_off   +i*sizeof(ushort) );
     445           0 :   }
     446           0 : }
     447             : 
     448             : /**********************************************************************/
     449             : /* Vote account state -- direct field accessors                       */
     450             : /* These read fields directly from raw bincode-encoded vote account   */
     451             : /* data without full deserialization.                                  */
     452             : /**********************************************************************/
     453             : 
     454             : int
     455             : fd_vote_account_node_pubkey( uchar const *  data,
     456             :                              ulong          data_sz,
     457         101 :                              fd_pubkey_t *  out ) {
     458         101 :   CHECK( data_sz>=WIRE_OFF_NODE_PUBKEY+32UL );
     459         101 :   fd_memcpy( out, data+WIRE_OFF_NODE_PUBKEY, 32UL );
     460         101 :   return 0;
     461         101 : }
     462             : 
     463             : int
     464             : fd_vote_account_commission_bps( uchar const * data,
     465             :                                 ulong         data_sz,
     466             :                                 int           commission_rate_in_basis_points,
     467         101 :                                 ushort *      out ) {
     468         101 :   uchar const * ptr       = data;
     469         101 :   ulong         remaining = data_sz;
     470             : 
     471         101 :   uint discriminant;
     472         101 :   READ_U32( discriminant, &ptr, &remaining );
     473             : 
     474         101 :   switch( discriminant ) {
     475           0 :     case fd_vote_state_versioned_enum_v1_14_11: /* fallthrough */
     476           0 :     case fd_vote_state_versioned_enum_v3:
     477           0 :       CHECK( data_sz>WIRE_OFF_V1V3_COMMISSION );
     478           0 :       *out = data[ WIRE_OFF_V1V3_COMMISSION ] * 100U;
     479           0 :       return 0;
     480           0 :     case fd_vote_state_versioned_enum_v5: {
     481           0 :       CHECK( data_sz==FD_VOTE_STATE_V5_SZ );
     482           0 :       fd_vote_state_v5_commission_t c;
     483           0 :       v5_commission_load( data, fd_commission_kind_enum_inflation_rewards, &c );
     484           0 :       CHECK( fd_vote_state_v5_commission_is_valid( &c ) );
     485           0 :       *out = fd_vote_state_v5_commission_latest( &c );
     486           0 :       if( !commission_rate_in_basis_points ) *out = (ushort)( (*out/100U)*100U );
     487           0 :       return 0;
     488           0 :     }
     489         101 :     case fd_vote_state_versioned_enum_v4:
     490         101 :       CHECK( data_sz>=WIRE_OFF_V4_COMMISSION_BPS+2UL );
     491         101 :       *out = FD_LOAD( ushort, data+WIRE_OFF_V4_COMMISSION_BPS );
     492             : 
     493             :       /* Round down to the nearest whole percentage if SIMD-0291 is not
     494             :          yet active. */
     495         101 :       if( !commission_rate_in_basis_points ) {
     496           0 :         *out = (*out / 100U) * 100U;
     497           0 :       }
     498         101 :       return 0;
     499           0 :     default:
     500           0 :       return 1;
     501         101 :   }
     502         101 : }
     503             : 
     504             : int
     505             : fd_vote_account_block_revenue_commission_bps( uchar const * data,
     506             :                                               ulong         data_sz,
     507         101 :                                               ushort *      out ) {
     508         101 :   uchar const * ptr       = data;
     509         101 :   ulong         remaining = data_sz;
     510             : 
     511         101 :   uint discriminant;
     512         101 :   READ_U32( discriminant, &ptr, &remaining );
     513             : 
     514         101 :   switch( discriminant ) {
     515           0 :     case fd_vote_state_versioned_enum_v1_14_11: /* fallthrough */
     516           0 :     case fd_vote_state_versioned_enum_v3:
     517           0 :       *out = FD_VOTE_DEFAULT_BLOCK_REVENUE_COMMISSION_BPS;
     518           0 :       return 0;
     519           0 :     case fd_vote_state_versioned_enum_v5: {
     520           0 :       CHECK( data_sz==FD_VOTE_STATE_V5_SZ );
     521           0 :       fd_vote_state_v5_commission_t c;
     522           0 :       v5_commission_load( data, fd_commission_kind_enum_block_revenue, &c );
     523           0 :       CHECK( fd_vote_state_v5_commission_is_valid( &c ) );
     524           0 :       *out = fd_vote_state_v5_commission_latest( &c );
     525           0 :       return 0;
     526           0 :     }
     527         101 :     case fd_vote_state_versioned_enum_v4:
     528         101 :       CHECK( data_sz>=WIRE_OFF_V4_BLOCK_REVENUE_COMMISSION_BPS+2UL );
     529         101 :       *out = FD_LOAD( ushort, data+WIRE_OFF_V4_BLOCK_REVENUE_COMMISSION_BPS );
     530         101 :       return 0;
     531           0 :     default:
     532           0 :       return 1;
     533         101 :   }
     534         101 : }
     535             : 
     536             : int
     537             : fd_vote_account_pending_delegator_rewards( uchar const * data,
     538             :                                            ulong         data_sz,
     539         101 :                                            ulong *       out ) {
     540         101 :   uchar const * ptr       = data;
     541         101 :   ulong         remaining = data_sz;
     542             : 
     543         101 :   uint discriminant;
     544         101 :   READ_U32( discriminant, &ptr, &remaining );
     545             : 
     546         101 :   switch( discriminant ) {
     547           0 :     case fd_vote_state_versioned_enum_v1_14_11: /* fallthrough */
     548           0 :     case fd_vote_state_versioned_enum_v3:
     549           0 :       *out = 0UL;
     550           0 :       return 0;
     551         101 :     case fd_vote_state_versioned_enum_v4:
     552         101 :       CHECK( data_sz==FD_VOTE_STATE_V4_SZ );
     553         101 :       *out = FD_LOAD( ulong, data+WIRE_OFF_V4_PENDING_DELEGATOR_REWARDS );
     554         101 :       return 0;
     555           0 :     case fd_vote_state_versioned_enum_v5:
     556           0 :       CHECK( data_sz==FD_VOTE_STATE_V5_SZ );
     557           0 :       *out = FD_LOAD( ulong, data+V5_OFF( pending_delegator_rewards ) );
     558           0 :       return 0;
     559           0 :     default:
     560           0 :       return 1;
     561         101 :   }
     562         101 : }
     563             : 
     564             : static ulong
     565             : pending_delegator_rewards_off( uchar const * data,
     566           0 :                                ulong         data_sz ) {
     567           0 :   if( data_sz<4UL ) return 0UL;
     568           0 :   switch( FD_LOAD( uint, data ) ) {
     569           0 :     case fd_vote_state_versioned_enum_v4: return fd_ulong_if( data_sz==FD_VOTE_STATE_V4_SZ, WIRE_OFF_V4_PENDING_DELEGATOR_REWARDS, 0UL );
     570           0 :     case fd_vote_state_versioned_enum_v5: return fd_ulong_if( data_sz==FD_VOTE_STATE_V5_SZ, V5_OFF( pending_delegator_rewards ), 0UL );
     571           0 :     default:                              return 0UL;
     572           0 :   }
     573           0 : }
     574             : 
     575             : int
     576             : fd_vote_account_add_pending_delegator_rewards( uchar * data,
     577             :                                                ulong   data_sz,
     578           0 :                                                ulong   lamports ) {
     579           0 :   ulong off = pending_delegator_rewards_off( data, data_sz );
     580           0 :   if( FD_UNLIKELY( !off ) ) return 1;
     581           0 :   ulong cur = FD_LOAD( ulong, data+off );
     582           0 :   ulong sum;
     583           0 :   if( FD_UNLIKELY( __builtin_uaddl_overflow( cur, lamports, &sum ) ) ) return 2;
     584           0 :   FD_STORE( ulong, data+off, sum );
     585           0 :   return 0;
     586           0 : }
     587             : 
     588             : int
     589             : fd_vote_account_reset_pending_delegator_rewards( uchar * data,
     590             :                                                  ulong   data_sz,
     591           0 :                                                  ulong * old_out ) {
     592           0 :   ulong off = pending_delegator_rewards_off( data, data_sz );
     593           0 :   if( FD_UNLIKELY( !off ) ) return 1;
     594           0 :   *old_out = FD_LOAD( ulong, data+off );
     595           0 :   FD_STORE( ulong, data+off, 0UL );
     596           0 :   return 0;
     597           0 : }
     598             : 
     599             : int
     600             : fd_vote_account_collectors( uchar const *       data,
     601             :                             ulong               data_sz,
     602             :                             fd_pubkey_t const * vote_pubkey,
     603             :                             fd_pubkey_t const * node_pubkey,
     604             :                             fd_pubkey_t *       inflation_rewards_collector_out,
     605         101 :                             fd_pubkey_t *       block_revenue_collector_out ) {
     606         101 :   uchar const * ptr       = data;
     607         101 :   ulong         remaining = data_sz;
     608             : 
     609         101 :   uint discriminant;
     610         101 :   READ_U32( discriminant, &ptr, &remaining );
     611             : 
     612         101 :   switch( discriminant ) {
     613           0 :     case fd_vote_state_versioned_enum_v1_14_11: /* fallthrough */
     614           0 :     case fd_vote_state_versioned_enum_v3:
     615             :       /* Pre-v4 vote states have no collector fields: default to the
     616             :          vote account (inflation) and the node identity (block
     617             :          revenue). */
     618           0 :       *inflation_rewards_collector_out = *vote_pubkey;
     619           0 :       *block_revenue_collector_out     = *node_pubkey;
     620           0 :       return 0;
     621           0 :     case fd_vote_state_versioned_enum_v5:
     622           0 :       CHECK( data_sz==FD_VOTE_STATE_V5_SZ );
     623           0 :       __attribute__((fallthrough));
     624         101 :     case fd_vote_state_versioned_enum_v4:
     625         101 :       CHECK( data_sz>=WIRE_OFF_V4_BLOCK_REVENUE_COLLECTOR+32UL );
     626         101 :       fd_memcpy( inflation_rewards_collector_out, data+WIRE_OFF_V4_INFLATION_REWARDS_COLLECTOR, 32UL );
     627         101 :       fd_memcpy( block_revenue_collector_out,     data+WIRE_OFF_V4_BLOCK_REVENUE_COLLECTOR,     32UL );
     628         101 :       return 0;
     629           0 :     default:
     630           0 :       return 1;
     631         101 :   }
     632         101 : }
     633             : 
     634             : /* Seeks past variable-length fields to the start of epoch_credits.
     635             :    On success sets *out_ptr to the first entry and *out_cnt to the
     636             :    entry count.  Returns 0 on success, 1 on error. */
     637             : 
     638             : /* seek_authorized_voters positions *out_ptr at the first entry of the
     639             :    authorized_voters map (sorted by ascending epoch, WIRE_AUTH_VOTER_SZ
     640             :    bytes each) and stores the entry count in *out_cnt.  All entries
     641             :    are bounds checked.  Returns 0 on success, 1 on malformed data. */
     642             : 
     643             : static int
     644             : seek_authorized_voters( uchar const *  data,
     645             :                         ulong          data_sz,
     646             :                         uint *         out_discriminant,
     647             :                         uchar const ** out_ptr,
     648             :                         ulong *        out_remaining,
     649         101 :                         ulong *        out_cnt ) {
     650         101 :   uchar const * ptr       = data;
     651         101 :   ulong         remaining = data_sz;
     652             : 
     653         101 :   uint discriminant;
     654         101 :   READ_U32( discriminant, &ptr, &remaining );
     655             : 
     656         101 :   switch( discriminant ) {
     657           0 :     case fd_vote_state_versioned_enum_v1_14_11: {
     658           0 :       SKIP_BYTES( WIRE_VOTES_OFF_V1_V3-4UL, &ptr, &remaining );
     659             : 
     660             :       /* Skip votes Vec<Lockout> */
     661           0 :       ulong votes_len;
     662           0 :       READ_U64( votes_len, &ptr, &remaining );
     663           0 :       CHECK_U64_MUL_OVERFLOW( votes_len, WIRE_LOCKOUT_SZ );
     664           0 :       SKIP_BYTES( votes_len*WIRE_LOCKOUT_SZ, &ptr, &remaining );
     665           0 :       break;
     666           0 :     }
     667             : 
     668           0 :     case fd_vote_state_versioned_enum_v3: {
     669           0 :       SKIP_BYTES( WIRE_VOTES_OFF_V1_V3-4UL, &ptr, &remaining );
     670             : 
     671             :       /* Skip votes Vec<LandedVote> */
     672           0 :       ulong votes_len;
     673           0 :       READ_U64( votes_len, &ptr, &remaining );
     674           0 :       CHECK_U64_MUL_OVERFLOW( votes_len, WIRE_LANDED_VOTE_SZ );
     675           0 :       SKIP_BYTES( votes_len*WIRE_LANDED_VOTE_SZ, &ptr, &remaining );
     676           0 :       break;
     677           0 :     }
     678             : 
     679         101 :     case fd_vote_state_versioned_enum_v4: {
     680         101 :       SKIP_BYTES( WIRE_BLS_OFF_V4-4UL, &ptr, &remaining );
     681             : 
     682             :       /* Skip Option<bls_pubkey_compressed> */
     683         101 :       uchar has_bls_pubkey;
     684         101 :       READ_U8( has_bls_pubkey, &ptr, &remaining );
     685         101 :       if( has_bls_pubkey ) {
     686         101 :         SKIP_BYTES( FD_BLS_PUBKEY_COMPRESSED_SZ, &ptr, &remaining );
     687         101 :       }
     688             : 
     689             :       /* Skip votes Vec<LandedVote> */
     690         101 :       ulong votes_len;
     691         101 :       READ_U64( votes_len, &ptr, &remaining );
     692         101 :       CHECK_U64_MUL_OVERFLOW( votes_len, WIRE_LANDED_VOTE_SZ );
     693         101 :       SKIP_BYTES( votes_len*WIRE_LANDED_VOTE_SZ, &ptr, &remaining );
     694         101 :       break;
     695         101 :     }
     696             : 
     697         101 :     case fd_vote_state_versioned_enum_v5: {
     698           0 :       CHECK( data_sz==FD_VOTE_STATE_V5_SZ );
     699           0 :       ulong cnt = FD_LOAD( uint, data+V5_OFF( authorized_voters_len ) );
     700           0 :       CHECK( cnt<=MAX_AUTHORIZED_VOTERS );
     701           0 :       *out_discriminant = discriminant;
     702           0 :       *out_ptr          = data+V5_OFF( authorized_voters );
     703           0 :       *out_remaining    = data_sz-V5_OFF( authorized_voters );
     704           0 :       *out_cnt          = cnt;
     705           0 :       return 0;
     706           0 :     }
     707             : 
     708           0 :     default:
     709           0 :       return 1;
     710         101 :   }
     711             : 
     712             :   /* Skip root_slot Option<u64> */
     713         101 :   uchar has_root_slot;
     714         101 :   READ_U8( has_root_slot, &ptr, &remaining );
     715         101 :   if( has_root_slot ) {
     716          30 :     SKIP_BYTES( 8UL, &ptr, &remaining );
     717          30 :   }
     718             : 
     719             :   /* Now at authorized_voters BTreeMap<u64, Pubkey> */
     720         101 :   ulong authorized_voters_len;
     721         101 :   READ_U64( authorized_voters_len, &ptr, &remaining );
     722         101 :   CHECK_U64_MUL_OVERFLOW( authorized_voters_len, WIRE_AUTH_VOTER_SZ );
     723         101 :   CHECK( authorized_voters_len*WIRE_AUTH_VOTER_SZ<=remaining );
     724             : 
     725         101 :   *out_discriminant = discriminant;
     726         101 :   *out_ptr          = ptr;
     727         101 :   *out_remaining    = remaining;
     728         101 :   *out_cnt          = authorized_voters_len;
     729         101 :   return 0;
     730         101 : }
     731             : 
     732             : static int
     733             : seek_epoch_credits( uchar const *                    data,
     734             :                     ulong                            data_sz,
     735             :                     fd_vote_epoch_credits_t const ** out_ptr,
     736         101 :                     ulong *                          out_cnt ) {
     737         101 :   if( data_sz>=4UL && FD_LOAD( uint, data )==fd_vote_state_versioned_enum_v5 ) {
     738           0 :     CHECK( data_sz==FD_VOTE_STATE_V5_SZ );
     739           0 :     ulong cnt = FD_LOAD( uint, data+V5_OFF( accumulated_lamports_len ) );
     740           0 :     CHECK( cnt<=FD_VOTE_STATE_V5_ACCUMULATED_LAMPORTS_MAX );
     741           0 :     *out_ptr = fd_type_pun_const( data+V5_OFF( accumulated_lamports ) );
     742           0 :     *out_cnt = cnt;
     743           0 :     return 0;
     744           0 :   }
     745             : 
     746         101 :   uint          discriminant;
     747         101 :   uchar const * ptr;
     748         101 :   ulong         remaining;
     749         101 :   ulong         authorized_voters_len;
     750         101 :   CHECK( !seek_authorized_voters( data, data_sz, &discriminant, &ptr, &remaining, &authorized_voters_len ) );
     751         101 :   SKIP_BYTES( authorized_voters_len*WIRE_AUTH_VOTER_SZ, &ptr, &remaining );
     752             : 
     753             :   /* Skip prior_voters (fixed size), absent in v4 */
     754         101 :   if( discriminant!=fd_vote_state_versioned_enum_v4 ) {
     755           0 :     SKIP_BYTES( WIRE_PRIOR_VOTERS_SZ, &ptr, &remaining );
     756           0 :   }
     757             : 
     758             :   /* Now at epoch_credits deque */
     759         101 :   ulong epoch_credits_len;
     760         101 :   READ_U64( epoch_credits_len, &ptr, &remaining );
     761         101 :   CHECK( epoch_credits_len<=MAX_EPOCH_CREDITS_HISTORY );
     762         101 :   CHECK_U64_MUL_OVERFLOW( epoch_credits_len, sizeof(fd_vote_epoch_credits_t) );
     763         101 :   CHECK( epoch_credits_len*sizeof(fd_vote_epoch_credits_t)<=remaining );
     764             : 
     765         101 :   *out_ptr = (fd_vote_epoch_credits_t const *)ptr;
     766         101 :   *out_cnt = epoch_credits_len;
     767         101 :   return 0;
     768         101 : }
     769             : 
     770             : int
     771             : fd_vote_account_authorized_voter( uchar const * data,
     772             :                                   ulong         data_sz,
     773             :                                   ulong         epoch,
     774           0 :                                   fd_pubkey_t * out ) {
     775           0 :   uint          discriminant;
     776           0 :   uchar const * ptr;
     777           0 :   ulong         remaining;
     778           0 :   ulong         cnt;
     779           0 :   CHECK( !seek_authorized_voters( data, data_sz, &discriminant, &ptr, &remaining, &cnt ) );
     780             : 
     781             :   /* Take the last entry whose epoch is at or before the requested one. */
     782           0 :   int found = 0;
     783           0 :   for( ulong i=0UL; i<cnt; i++, ptr+=WIRE_AUTH_VOTER_SZ ) {
     784           0 :     if( FD_LOAD( ulong, ptr )>epoch ) continue;
     785           0 :     fd_memcpy( out, ptr+8UL, 32UL );
     786           0 :     found = 1;
     787           0 :   }
     788             : 
     789           0 :   return !found;
     790           0 : }
     791             : 
     792             : int
     793             : fd_vote_account_last_timestamp( uchar const *             data,
     794             :                                 ulong                     data_sz,
     795           0 :                                 fd_vote_block_timestamp_t * out ) {
     796           0 :   if( data_sz>=4UL && FD_LOAD( uint, data )==fd_vote_state_versioned_enum_v5 ) {
     797           0 :     CHECK( data_sz==FD_VOTE_STATE_V5_SZ );
     798           0 :     fd_memcpy( out, data+V5_OFF( last_timestamp ), sizeof(fd_vote_block_timestamp_t) );
     799           0 :     return 0;
     800           0 :   }
     801             : 
     802           0 :   fd_vote_epoch_credits_t const * epoch_credits_ptr;
     803           0 :   ulong                           epoch_credits_len;
     804           0 :   CHECK( !seek_epoch_credits( data, data_sz, &epoch_credits_ptr, &epoch_credits_len ) );
     805             : 
     806           0 :   uchar const * timestamp_ptr = (uchar const *)( epoch_credits_ptr + epoch_credits_len );
     807           0 :   CHECK( timestamp_ptr+sizeof(fd_vote_block_timestamp_t)<=data+data_sz );
     808             : 
     809           0 :   fd_memcpy( out, timestamp_ptr, sizeof(fd_vote_block_timestamp_t) );
     810           0 :   return 0;
     811           0 : }
     812             : 
     813             : int
     814             : fd_vote_account_has_bls_pubkey( uchar const * data,
     815         306 :                                 ulong         data_sz ) {
     816         306 :   if( FD_UNLIKELY( data_sz<4UL ) ) return 0;
     817         306 :   uint discriminant = FD_LOAD( uint, data );
     818         306 :   if( discriminant==fd_vote_state_versioned_enum_v5 ) return data_sz==FD_VOTE_STATE_V5_SZ;
     819         306 :   if( discriminant!=fd_vote_state_versioned_enum_v4 ) return 0;
     820         306 :   if( FD_UNLIKELY( data_sz<WIRE_BLS_OFF_V4+1UL ) ) return 0;
     821         306 :   return !!data[ WIRE_BLS_OFF_V4 ];
     822         306 : }
     823             : 
     824             : int
     825             : fd_vote_account_bls_pubkey( uchar const * data,
     826             :                             ulong         data_sz,
     827         101 :                             uchar         out[ static FD_BLS_PUBKEY_COMPRESSED_SZ ] ) {
     828         101 :   if( FD_UNLIKELY( !fd_vote_account_has_bls_pubkey( data, data_sz ) ) ) return 1;
     829         101 :   if( FD_LOAD( uint, data )==fd_vote_state_versioned_enum_v5 ) {
     830           0 :     fd_memcpy( out, data+V5_OFF( bls_pubkey_compressed ), FD_BLS_PUBKEY_COMPRESSED_SZ );
     831           0 :     return 0;
     832           0 :   }
     833         101 :   if( FD_UNLIKELY( data_sz<WIRE_BLS_OFF_V4+1UL+FD_BLS_PUBKEY_COMPRESSED_SZ ) ) return 1;
     834         101 :   fd_memcpy( out, data+WIRE_BLS_OFF_V4+1UL, FD_BLS_PUBKEY_COMPRESSED_SZ );
     835         101 :   return 0;
     836         101 : }
     837             : 
     838             : fd_vote_epoch_credits_t const *
     839             : fd_vote_account_epoch_credits( uchar const * data,
     840             :                                ulong         data_sz,
     841         101 :                                ulong *       cnt ) {
     842         101 :   fd_vote_epoch_credits_t const * ptr;
     843         101 :   CHECK_RET_NULL( !seek_epoch_credits( data, data_sz, &ptr, cnt ) );
     844         101 :   return ptr;
     845         101 : }
     846             : 
     847             : /**********************************************************************/
     848             : /* Vote account state -- public API                                   */
     849             : /**********************************************************************/
     850             : 
     851             : int
     852             : fd_vote_account_is_valid_v5( uchar const * data,
     853         102 :                              ulong         data_sz ) {
     854         102 :   if( FD_UNLIKELY( data_sz!=FD_VOTE_STATE_V5_SZ || FD_LOAD( uint, data )!=fd_vote_state_versioned_enum_v5 ) ) return 0;
     855             : 
     856           0 :   fd_validator_location_t location = {
     857           0 :     .x = FD_LOAD( int, data+V5_OFF( location.x ) ),
     858           0 :     .y = FD_LOAD( int, data+V5_OFF( location.y ) ),
     859           0 :     .z = FD_LOAD( int, data+V5_OFF( location.z ) )
     860           0 :   };
     861           0 :   if( FD_UNLIKELY( !fd_validator_location_is_valid( &location ) ) ) return 0;
     862             : 
     863           0 :   fd_vote_state_v5_commission_t commission;
     864           0 :   v5_commission_load( data, fd_commission_kind_enum_inflation_rewards, &commission );
     865           0 :   if( FD_UNLIKELY( !fd_vote_state_v5_commission_is_valid( &commission ) ) ) return 0;
     866           0 :   v5_commission_load( data, fd_commission_kind_enum_block_revenue, &commission );
     867           0 :   if( FD_UNLIKELY( !fd_vote_state_v5_commission_is_valid( &commission ) ) ) return 0;
     868             : 
     869           0 :   ulong         av_len = FD_LOAD( uint, data+V5_OFF( authorized_voters_len ) );
     870           0 :   uchar const * av     = data+V5_OFF( authorized_voters );
     871           0 :   if( FD_UNLIKELY( av_len>MAX_AUTHORIZED_VOTERS ) ) return 0;
     872           0 :   for( ulong i=1UL; i<av_len; i++ ) {
     873           0 :     if( FD_UNLIKELY( FD_LOAD( ulong, av+i*WIRE_AUTH_VOTER_SZ )<=FD_LOAD( ulong, av+(i-1UL)*WIRE_AUTH_VOTER_SZ ) ) ) return 0;
     874           0 :   }
     875             : 
     876           0 :   ulong         ec_len = FD_LOAD( uint, data+V5_OFF( accumulated_lamports_len ) );
     877           0 :   uchar const * ec     = data+V5_OFF( accumulated_lamports );
     878           0 :   if( FD_UNLIKELY( ec_len>FD_VOTE_STATE_V5_ACCUMULATED_LAMPORTS_MAX ) ) return 0;
     879             : 
     880           0 :   if( FD_UNLIKELY( FD_LOAD( ulong, data+V5_OFF( rank_epoch ) )!=ULONG_MAX || FD_LOAD( ushort, data+V5_OFF( rank ) )!=USHORT_MAX ) ) return 0;
     881             : 
     882           0 :   return fd_mem_iszero( av+av_len*WIRE_AUTH_VOTER_SZ, (MAX_AUTHORIZED_VOTERS-av_len)*WIRE_AUTH_VOTER_SZ ) &&
     883           0 :          fd_mem_iszero( ec+ec_len*sizeof(fd_vote_epoch_credits_t),
     884           0 :                         (FD_VOTE_STATE_V5_ACCUMULATED_LAMPORTS_MAX-ec_len)*sizeof(fd_vote_epoch_credits_t) ) &&
     885           0 :          fd_mem_iszero( data+FD_VOTE_STATE_V5_PAD_OFF, FD_VOTE_STATE_V5_PAD_SZ );
     886           0 : }
     887             : 
     888             : fd_validator_location_t
     889             : fd_vote_account_location( uchar const * data,
     890         101 :                           ulong         data_sz ) {
     891         101 :   if( !fd_vote_account_is_valid_v5( data, data_sz ) ) return (fd_validator_location_t){0};
     892           0 :   return FD_LOAD( fd_validator_location_t, data+V5_OFF( location ) );
     893         101 : }
     894             : 
     895             : fd_vote_state_versioned_t *
     896             : fd_vote_state_versioned_new( fd_vote_state_versioned_t * self,
     897        4156 :                              uint                        kind ) {
     898        4156 :   if( FD_UNLIKELY( !self ) ) {
     899           0 :     FD_LOG_WARNING(( "NULL mem" ));
     900           0 :     return NULL;
     901           0 :   }
     902             : 
     903             :   /* Init and join data structures */
     904        4156 :   fd_landed_vote_t * votes = deq_fd_landed_vote_t_join(
     905        4156 :     deq_fd_landed_vote_t_new( self->landed_votes_mem, MAX_LOCKOUT_HISTORY_CAPACITY )
     906        4156 :   );
     907        4156 :   fd_vote_epoch_credits_t * epoch_credits = deq_fd_vote_epoch_credits_t_join(
     908        4156 :     deq_fd_vote_epoch_credits_t_new( self->epoch_credits_mem )
     909        4156 :   );
     910        4156 :   fd_vote_authorized_voter_t * authorized_voters_pool  = fd_vote_authorized_voters_pool_join(
     911        4156 :     fd_vote_authorized_voters_pool_new( self->authorized_voters_pool_mem, MAX_AUTHORIZED_VOTERS_CAPACITY )
     912        4156 :   );
     913        4156 :   fd_vote_authorized_voters_treap_t * authorized_voters_treap = fd_vote_authorized_voters_treap_join(
     914        4156 :     fd_vote_authorized_voters_treap_new( self->authorized_voters_treap_mem, MAX_AUTHORIZED_VOTERS_CAPACITY )
     915        4156 :   );
     916             : 
     917        4156 :   self->kind = kind;
     918        4156 :   switch( kind ) {
     919         825 :     case fd_vote_state_versioned_enum_uninitialized:
     920         825 :       break;
     921         281 :     case fd_vote_state_versioned_enum_v1_14_11:
     922         281 :       memset( &self->v1_14_11, 0, sizeof(fd_vote_state_1_14_11_t) );
     923         281 :       self->v1_14_11.votes                   = votes;
     924         281 :       self->v1_14_11.epoch_credits           = epoch_credits;
     925         281 :       self->v1_14_11.authorized_voters.pool  = authorized_voters_pool;
     926         281 :       self->v1_14_11.authorized_voters.treap = authorized_voters_treap;
     927         281 :       break;
     928        1441 :     case fd_vote_state_versioned_enum_v3:
     929        1441 :       memset( &self->v3, 0, sizeof(fd_vote_state_v3_t) );
     930        1441 :       self->v3.votes                   = votes;
     931        1441 :       self->v3.epoch_credits           = epoch_credits;
     932        1441 :       self->v3.authorized_voters.pool  = authorized_voters_pool;
     933        1441 :       self->v3.authorized_voters.treap = authorized_voters_treap;
     934        1441 :       break;
     935        1424 :     case fd_vote_state_versioned_enum_v4:
     936        1424 :       memset( &self->v4, 0, sizeof(fd_vote_state_v4_t) );
     937        1424 :       self->v4.votes                    = votes;
     938        1424 :       self->v4.epoch_credits            = epoch_credits;
     939        1424 :       self->v4.authorized_voters.pool   = authorized_voters_pool;
     940        1424 :       self->v4.authorized_voters.treap  = authorized_voters_treap;
     941        1424 :       break;
     942           1 :     case fd_vote_state_versioned_enum_v5:
     943           1 :       memset( &self->v5, 0, sizeof(fd_vote_state_v5_t) );
     944           1 :       self->v5.inflation_rewards_commission_bps_len = 1;
     945           1 :       self->v5.block_revenue_commission_bps_len     = 1;
     946           1 :       self->v5.latest_vote                          = ULONG_MAX;
     947           1 :       self->v5.root_slot                            = ULONG_MAX;
     948           1 :       self->v5.rank_epoch                           = ULONG_MAX;
     949           1 :       self->v5.rank                                 = USHORT_MAX;
     950           1 :       break;
     951         192 :     default:
     952         192 :       return NULL;
     953        4156 :   }
     954             : 
     955        3965 :   return self;
     956        4156 : }
     957             : 
     958             : static int
     959             : fd_vote_state_versioned_deserialize_inner( fd_vote_state_versioned_t * self,
     960             :                                            uchar const *               payload,
     961        4014 :                                            ulong                       payload_sz ) {
     962        4014 :   CHECK( self!=NULL );
     963        4014 :   CHECK( payload!=NULL );
     964             : 
     965        4014 :   uchar const * ptr = payload;
     966        4014 :   ulong         rem = payload_sz;
     967             : 
     968        4014 :   uint kind;
     969        4014 :   READ_U32( kind, &ptr, &rem );
     970             : 
     971        3989 :   CHECK( fd_vote_state_versioned_new( self, kind )!=NULL );
     972             : 
     973        3797 :   switch( self->kind ) {
     974         825 :     case fd_vote_state_versioned_enum_uninitialized:
     975             :       /* No-op, nothing to decode */
     976         825 :       return 0;
     977             : 
     978         281 :     case fd_vote_state_versioned_enum_v1_14_11: {
     979         281 :       READ_PUBKEY( self->v1_14_11.node_pubkey, &ptr, &rem );
     980         278 :       READ_PUBKEY( self->v1_14_11.authorized_withdrawer, &ptr, &rem );
     981         272 :       READ_U8( self->v1_14_11.commission, &ptr, &rem );
     982         270 :       CHECK( !deser_votes( self->v1_14_11.votes, 1, &ptr, &rem ) ); /* v1_14_11 wire format is Lockout, not LandedVote */
     983         248 :       CHECK( !deser_root_slot( &self->v1_14_11.has_root_slot, &self->v1_14_11.root_slot, &ptr, &rem ) );
     984         239 :       CHECK( !deser_authorized_voters( self->v1_14_11.authorized_voters.pool, self->v1_14_11.authorized_voters.treap, &ptr, &rem ) );
     985         179 :       CHECK( !deser_prior_voters( &self->v1_14_11.prior_voters, &ptr, &rem ) );
     986         150 :       CHECK( !deser_epoch_credits( self->v1_14_11.epoch_credits, &ptr, &rem ) );
     987         147 :       READ_BYTES( &self->v1_14_11.last_timestamp, sizeof(fd_vote_block_timestamp_t), &ptr, &rem );
     988         145 :       break;
     989         147 :     }
     990             : 
     991        1442 :     case fd_vote_state_versioned_enum_v3: {
     992        1442 :       READ_PUBKEY( self->v3.node_pubkey, &ptr, &rem );
     993        1431 :       READ_PUBKEY( self->v3.authorized_withdrawer, &ptr, &rem );
     994        1427 :       READ_U8( self->v3.commission, &ptr, &rem );
     995        1424 :       CHECK( !deser_votes( self->v3.votes, 0, &ptr, &rem ) );
     996        1395 :       CHECK( !deser_root_slot( &self->v3.has_root_slot, &self->v3.root_slot, &ptr, &rem ) );
     997        1387 :       CHECK( !deser_authorized_voters( self->v3.authorized_voters.pool, self->v3.authorized_voters.treap, &ptr, &rem ) );
     998        1304 :       CHECK( !deser_prior_voters( &self->v3.prior_voters, &ptr, &rem ) );
     999        1279 :       CHECK( !deser_epoch_credits( self->v3.epoch_credits, &ptr, &rem ) );
    1000        1274 :       READ_BYTES( &self->v3.last_timestamp, sizeof(fd_vote_block_timestamp_t), &ptr, &rem );
    1001        1270 :       break;
    1002        1274 :     }
    1003             : 
    1004        1270 :     case fd_vote_state_versioned_enum_v4: {
    1005        1259 :       READ_PUBKEY( self->v4.node_pubkey, &ptr, &rem );
    1006        1257 :       READ_PUBKEY( self->v4.authorized_withdrawer, &ptr, &rem );
    1007        1257 :       READ_PUBKEY( self->v4.inflation_rewards_collector, &ptr, &rem );
    1008        1257 :       READ_PUBKEY( self->v4.block_revenue_collector, &ptr, &rem );
    1009        1257 :       READ_U16( self->v4.inflation_rewards_commission_bps, &ptr, &rem );
    1010        1257 :       READ_U16( self->v4.block_revenue_commission_bps, &ptr, &rem );
    1011        1257 :       READ_U64( self->v4.pending_delegator_rewards, &ptr, &rem );
    1012             : 
    1013             :       /* Option<[u8; 48]> */
    1014        1257 :       READ_OPTION( self->v4.has_bls_pubkey_compressed, &ptr, &rem );
    1015        1257 :       if( self->v4.has_bls_pubkey_compressed ) {
    1016         350 :         READ_BYTES( self->v4.bls_pubkey_compressed, FD_BLS_PUBKEY_COMPRESSED_SZ, &ptr, &rem );
    1017         350 :       }
    1018             : 
    1019        1257 :       CHECK( !deser_votes( self->v4.votes, 0, &ptr, &rem ) );
    1020        1257 :       CHECK( !deser_root_slot( &self->v4.has_root_slot, &self->v4.root_slot, &ptr, &rem ) );
    1021        1257 :       CHECK( !deser_authorized_voters( self->v4.authorized_voters.pool, self->v4.authorized_voters.treap, &ptr, &rem ) );
    1022        1257 :       CHECK( !deser_epoch_credits( self->v4.epoch_credits, &ptr, &rem ) ); /* v4 has no prior_voters */
    1023        1257 :       READ_BYTES( &self->v4.last_timestamp, sizeof(fd_vote_block_timestamp_t), &ptr, &rem );
    1024        1257 :       break;
    1025        1257 :     }
    1026             : 
    1027        1257 :     case fd_vote_state_versioned_enum_v5:
    1028           1 :       CHECK( fd_vote_account_is_valid_v5( payload, payload_sz ) );
    1029           0 :       fd_memcpy( &self->v5, ptr, sizeof(fd_vote_state_v5_t) );
    1030           0 :       break;
    1031             : 
    1032           0 :     default:
    1033           0 :       return 1;
    1034        3797 :   }
    1035             : 
    1036        2671 :   return 0;
    1037        3797 : }
    1038             : 
    1039             : fd_vote_state_versioned_t *
    1040             : fd_vote_state_versioned_deserialize( fd_vote_state_versioned_t * self,
    1041             :                                      uchar const *               payload,
    1042        4013 :                                      ulong                       payload_sz ) {
    1043        4013 :   CHECK_RET_NULL( !fd_vote_state_versioned_deserialize_inner( self, payload, payload_sz ) );
    1044        3485 :   return self;
    1045        4013 : }
    1046             : 
    1047             : int
    1048             : fd_vote_state_versioned_serialize( fd_vote_state_versioned_t const * self,
    1049             :                                    uchar *                           buf,
    1050        1086 :                                    ulong                             buf_sz ) {
    1051        1086 :   CHECK( self!=NULL );
    1052        1086 :   CHECK( buf!=NULL );
    1053             : 
    1054        1086 :   uchar * out    = buf;
    1055        1086 :   ulong   out_sz = buf_sz;
    1056             : 
    1057        1086 :   WRITE_U32( self->kind, &out, &out_sz );
    1058             : 
    1059        1086 :   switch( self->kind ) {
    1060           0 :     case fd_vote_state_versioned_enum_uninitialized:
    1061             :       /* No-op, nothing to encode */
    1062           0 :       return 0;
    1063             : 
    1064           0 :     case fd_vote_state_versioned_enum_v1_14_11: {
    1065           0 :       WRITE_PUBKEY( self->v1_14_11.node_pubkey, &out, &out_sz );
    1066           0 :       WRITE_PUBKEY( self->v1_14_11.authorized_withdrawer, &out, &out_sz );
    1067           0 :       WRITE_U8( self->v1_14_11.commission, &out, &out_sz );
    1068           0 :       CHECK( !ser_votes( self->v1_14_11.votes, 1, &out, &out_sz ) ); /* v1_14_11 wire format is Lockout, not LandedVote */
    1069           0 :       CHECK( !ser_root_slot( self->v1_14_11.has_root_slot, self->v1_14_11.root_slot, &out, &out_sz ) );
    1070           0 :       CHECK( !ser_authorized_voters( self->v1_14_11.authorized_voters.pool, self->v1_14_11.authorized_voters.treap, &out, &out_sz ) );
    1071           0 :       CHECK( !ser_prior_voters( &self->v1_14_11.prior_voters, &out, &out_sz ) );
    1072           0 :       CHECK( !ser_epoch_credits( self->v1_14_11.epoch_credits, &out, &out_sz ) );
    1073           0 :       WRITE_BYTES( &self->v1_14_11.last_timestamp, sizeof(fd_vote_block_timestamp_t), &out, &out_sz );
    1074           0 :       break;
    1075           0 :     }
    1076             : 
    1077           0 :     case fd_vote_state_versioned_enum_v3: {
    1078           0 :       WRITE_PUBKEY( self->v3.node_pubkey, &out, &out_sz );
    1079           0 :       WRITE_PUBKEY( self->v3.authorized_withdrawer, &out, &out_sz );
    1080           0 :       WRITE_U8( self->v3.commission, &out, &out_sz );
    1081           0 :       CHECK( !ser_votes( self->v3.votes, 0, &out, &out_sz ) );
    1082           0 :       CHECK( !ser_root_slot( self->v3.has_root_slot, self->v3.root_slot, &out, &out_sz ) );
    1083           0 :       CHECK( !ser_authorized_voters( self->v3.authorized_voters.pool, self->v3.authorized_voters.treap, &out, &out_sz ) );
    1084           0 :       CHECK( !ser_prior_voters( &self->v3.prior_voters, &out, &out_sz ) );
    1085           0 :       CHECK( !ser_epoch_credits( self->v3.epoch_credits, &out, &out_sz ) );
    1086           0 :       WRITE_BYTES( &self->v3.last_timestamp, sizeof(fd_vote_block_timestamp_t), &out, &out_sz );
    1087           0 :       break;
    1088           0 :     }
    1089             : 
    1090        1086 :     case fd_vote_state_versioned_enum_v4: {
    1091        1086 :       WRITE_PUBKEY( self->v4.node_pubkey, &out, &out_sz );
    1092        1086 :       WRITE_PUBKEY( self->v4.authorized_withdrawer, &out, &out_sz );
    1093        1086 :       WRITE_PUBKEY( self->v4.inflation_rewards_collector, &out, &out_sz );
    1094        1086 :       WRITE_PUBKEY( self->v4.block_revenue_collector, &out, &out_sz );
    1095        1086 :       WRITE_U16( self->v4.inflation_rewards_commission_bps, &out, &out_sz );
    1096        1086 :       WRITE_U16( self->v4.block_revenue_commission_bps, &out, &out_sz );
    1097        1086 :       WRITE_U64( self->v4.pending_delegator_rewards, &out, &out_sz );
    1098             : 
    1099             :       /* Option<[u8; 48]> */
    1100        1086 :       WRITE_OPTION( self->v4.has_bls_pubkey_compressed, &out, &out_sz );
    1101        1086 :       if( self->v4.has_bls_pubkey_compressed ) {
    1102         103 :         WRITE_BYTES( self->v4.bls_pubkey_compressed, FD_BLS_PUBKEY_COMPRESSED_SZ, &out, &out_sz );
    1103         103 :       }
    1104             : 
    1105        1086 :       CHECK( !ser_votes( self->v4.votes, 0, &out, &out_sz ) );
    1106        1086 :       CHECK( !ser_root_slot( self->v4.has_root_slot, self->v4.root_slot, &out, &out_sz ) );
    1107        1086 :       CHECK( !ser_authorized_voters( self->v4.authorized_voters.pool, self->v4.authorized_voters.treap, &out, &out_sz ) );
    1108        1086 :       CHECK( !ser_epoch_credits( self->v4.epoch_credits, &out, &out_sz ) ); /* v4 has no prior_voters */
    1109        1086 :       WRITE_BYTES( &self->v4.last_timestamp, sizeof(fd_vote_block_timestamp_t), &out, &out_sz );
    1110        1086 :       break;
    1111        1086 :     }
    1112             : 
    1113        1086 :     case fd_vote_state_versioned_enum_v5: {
    1114           0 :       fd_validator_location_t       location = self->v5.location;
    1115           0 :       fd_vote_state_v5_commission_t inflation_commission;
    1116           0 :       fd_vote_state_v5_commission_t block_commission;
    1117           0 :       fd_vote_state_v5_commission_get( &self->v5, fd_commission_kind_enum_inflation_rewards, &inflation_commission );
    1118           0 :       fd_vote_state_v5_commission_get( &self->v5, fd_commission_kind_enum_block_revenue,     &block_commission     );
    1119           0 :       CHECK( buf_sz>=FD_VOTE_STATE_V5_SZ );
    1120           0 :       CHECK( self->v5.authorized_voters_len<=MAX_AUTHORIZED_VOTERS );
    1121           0 :       CHECK( self->v5.accumulated_lamports_len<=FD_VOTE_STATE_V5_ACCUMULATED_LAMPORTS_MAX );
    1122           0 :       CHECK( fd_validator_location_is_valid( &location ) );
    1123           0 :       CHECK( fd_vote_state_v5_commission_is_valid( &inflation_commission ) );
    1124           0 :       CHECK( fd_vote_state_v5_commission_is_valid( &block_commission     ) );
    1125           0 :       CHECK( self->v5.rank_epoch==ULONG_MAX && self->v5.rank==USHORT_MAX );
    1126           0 :       fd_memcpy( out, &self->v5, offsetof( fd_vote_state_v5_t, _pad ) );
    1127           0 :       fd_memset( out+offsetof( fd_vote_state_v5_t, _pad ), 0, FD_VOTE_STATE_V5_PAD_SZ );
    1128           0 :       break;
    1129           0 :     }
    1130             : 
    1131           0 :     default:
    1132           0 :       return 1;
    1133        1086 :   }
    1134             : 
    1135        1085 :   return 0;
    1136        1086 : }
    1137             : 
    1138             : ulong
    1139        1086 : fd_vote_state_versioned_serialized_size( fd_vote_state_versioned_t const * self ) {
    1140        1086 :   switch( self->kind ) {
    1141             : 
    1142           0 :     case fd_vote_state_versioned_enum_uninitialized:
    1143           0 :       return 4UL;
    1144             : 
    1145           0 :     case fd_vote_state_versioned_enum_v1_14_11: {
    1146           0 :       ulong votes_cnt         = self->v1_14_11.votes                   ? deq_fd_landed_vote_t_cnt( self->v1_14_11.votes )                                  : 0UL;
    1147           0 :       ulong auth_voters_cnt   = self->v1_14_11.authorized_voters.treap ? fd_vote_authorized_voters_treap_ele_cnt( self->v1_14_11.authorized_voters.treap ) : 0UL;
    1148           0 :       ulong epoch_credits_cnt = self->v1_14_11.epoch_credits           ? deq_fd_vote_epoch_credits_t_cnt( self->v1_14_11.epoch_credits )                   : 0UL;
    1149           0 :       return WIRE_VOTES_OFF_V1_V3
    1150           0 :            + 8UL + votes_cnt * WIRE_LOCKOUT_SZ
    1151           0 :            + 1UL + (ulong)self->v1_14_11.has_root_slot * 8UL
    1152           0 :            + 8UL + auth_voters_cnt * WIRE_AUTH_VOTER_SZ
    1153           0 :            + WIRE_PRIOR_VOTERS_SZ
    1154           0 :            + 8UL + epoch_credits_cnt * sizeof(fd_vote_epoch_credits_t)
    1155           0 :            + sizeof(fd_vote_block_timestamp_t);
    1156           0 :     }
    1157             : 
    1158           0 :     case fd_vote_state_versioned_enum_v3: {
    1159           0 :       ulong votes_cnt         = self->v3.votes                   ? deq_fd_landed_vote_t_cnt( self->v3.votes )                                  : 0UL;
    1160           0 :       ulong auth_voters_cnt   = self->v3.authorized_voters.treap ? fd_vote_authorized_voters_treap_ele_cnt( self->v3.authorized_voters.treap )  : 0UL;
    1161           0 :       ulong epoch_credits_cnt = self->v3.epoch_credits           ? deq_fd_vote_epoch_credits_t_cnt( self->v3.epoch_credits )                    : 0UL;
    1162           0 :       return WIRE_VOTES_OFF_V1_V3
    1163           0 :            + 8UL + votes_cnt * WIRE_LANDED_VOTE_SZ
    1164           0 :            + 1UL + (ulong)self->v3.has_root_slot * 8UL
    1165           0 :            + 8UL + auth_voters_cnt * WIRE_AUTH_VOTER_SZ
    1166           0 :            + WIRE_PRIOR_VOTERS_SZ
    1167           0 :            + 8UL + epoch_credits_cnt * sizeof(fd_vote_epoch_credits_t)
    1168           0 :            + sizeof(fd_vote_block_timestamp_t);
    1169           0 :     }
    1170             : 
    1171        1086 :     case fd_vote_state_versioned_enum_v4: {
    1172        1086 :       ulong votes_cnt         = self->v4.votes                   ? deq_fd_landed_vote_t_cnt( self->v4.votes )                                  : 0UL;
    1173        1086 :       ulong auth_voters_cnt   = self->v4.authorized_voters.treap ? fd_vote_authorized_voters_treap_ele_cnt( self->v4.authorized_voters.treap )  : 0UL;
    1174        1086 :       ulong epoch_credits_cnt = self->v4.epoch_credits           ? deq_fd_vote_epoch_credits_t_cnt( self->v4.epoch_credits )                    : 0UL;
    1175        1086 :       return WIRE_BLS_OFF_V4
    1176        1086 :            + 1UL + (ulong)self->v4.has_bls_pubkey_compressed * FD_BLS_PUBKEY_COMPRESSED_SZ
    1177        1086 :            + 8UL + votes_cnt * WIRE_LANDED_VOTE_SZ
    1178        1086 :            + 1UL + (ulong)self->v4.has_root_slot * 8UL
    1179        1086 :            + 8UL + auth_voters_cnt * WIRE_AUTH_VOTER_SZ
    1180        1086 :            + 8UL + epoch_credits_cnt * sizeof(fd_vote_epoch_credits_t)
    1181        1086 :            + sizeof(fd_vote_block_timestamp_t);
    1182           0 :     }
    1183             : 
    1184           0 :     case fd_vote_state_versioned_enum_v5:
    1185           0 :       return FD_VOTE_STATE_V5_SZ;
    1186             : 
    1187           0 :     default:
    1188           0 :       return 0UL;
    1189        1086 :   }
    1190        1086 : }
    1191             : 
    1192             : /**********************************************************************/
    1193             : /* Vote instruction -- per-variant deserializers                      */
    1194             : /**********************************************************************/
    1195             : 
    1196             : static int
    1197             : deser_vote_authorize( fd_vote_authorize_t * out,
    1198             :                       uchar const **        data,
    1199        1127 :                       ulong *               sz ) {
    1200        1127 :   READ_ENUM( out->discriminant, 3U, data, sz );
    1201        1117 :   if( out->discriminant==fd_vote_authorize_enum_voter_with_bls ) {
    1202         199 :     READ_BYTES( out->voter_with_bls.bls_pubkey, FD_BLS_PUBKEY_COMPRESSED_SZ, data, sz );
    1203         193 :     READ_BYTES( out->voter_with_bls.bls_proof_of_possession, FD_BLS_PROOF_OF_POSSESSION_COMPRESSED_SZ, data, sz );
    1204         193 :   }
    1205        1105 :   return 0;
    1206        1117 : }
    1207             : 
    1208             : static int
    1209             : deser_vote_init( fd_vote_init_t * out,
    1210             :                  uchar const **   data,
    1211         233 :                  ulong *          sz ) {
    1212         233 :   READ_BYTES( out, sizeof(fd_vote_init_t), data, sz );
    1213         190 :   return 0;
    1214         233 : }
    1215             : 
    1216             : static int
    1217             : deser_vote_init_v2( fd_vote_init_v2_t * out,
    1218             :                     uchar const **      data,
    1219           1 :                     ulong *             sz ) {
    1220           1 :   READ_PUBKEY( out->node_pubkey, data, sz );
    1221           1 :   READ_PUBKEY( out->authorized_voter, data, sz );
    1222           1 :   READ_BYTES( out->authorized_voter_bls_pubkey, FD_BLS_PUBKEY_COMPRESSED_SZ, data, sz );
    1223           1 :   READ_BYTES( out->authorized_voter_bls_proof_of_possession, FD_BLS_PROOF_OF_POSSESSION_COMPRESSED_SZ, data, sz );
    1224           1 :   READ_PUBKEY( out->authorized_withdrawer, data, sz );
    1225           1 :   READ_U16( out->inflation_rewards_commission_bps, data, sz );
    1226           1 :   READ_PUBKEY( out->inflation_rewards_collector, data, sz );
    1227           1 :   READ_U16( out->block_revenue_commission_bps, data, sz );
    1228           1 :   READ_PUBKEY( out->block_revenue_collector, data, sz );
    1229           1 :   return 0;
    1230           1 : }
    1231             : 
    1232             : static int
    1233             : deser_vote( fd_legacy_vote_t * out,
    1234             :             uchar const **     data,
    1235         571 :             ulong *            sz ) {
    1236         571 :   ulong slots_len;
    1237         571 :   READ_U64( slots_len, data, sz );
    1238         569 :   CHECK( slots_len<=FD_VOTE_INSTR_MAX_SLOT_NUMS_LEN );
    1239             : 
    1240         564 :   out->slots = deq_ulong_join( deq_ulong_new( out->slots_mem, FD_VOTE_INSTR_MAX_SLOT_NUMS_LEN ) );
    1241             : 
    1242       29182 :   for( ulong i=0UL; i<slots_len; i++ ) {
    1243       28648 :     ulong * elem = deq_ulong_push_tail_nocopy( out->slots );
    1244       28648 :     READ_U64( *elem, data, sz );
    1245       28648 :   }
    1246             : 
    1247         534 :   READ_HASH( out->hash, data, sz );
    1248         525 :   READ_OPTION( out->has_timestamp, data, sz );
    1249         520 :   if( out->has_timestamp ) {
    1250         243 :     READ_I64( out->timestamp, data, sz );
    1251         243 :   }
    1252         516 :   return 0;
    1253         520 : }
    1254             : 
    1255             : static int
    1256             : deser_vote_state_update( fd_vote_state_update_t * out,
    1257             :                          uchar const **           data,
    1258         897 :                          ulong *                  sz ) {
    1259         897 :   ulong lockouts_len;
    1260         897 :   READ_U64( lockouts_len, data, sz );
    1261         897 :   CHECK( lockouts_len<=FD_VOTE_INSTR_MAX_LOCKOUTS_LEN );
    1262             : 
    1263         893 :   out->lockouts = deq_fd_vote_lockout_t_join( deq_fd_vote_lockout_t_new( out->lockouts_mem, FD_VOTE_INSTR_MAX_LOCKOUTS_LEN ) );
    1264             : 
    1265       35335 :   for( ulong i=0; i<lockouts_len; i++ ) {
    1266       34489 :     fd_vote_lockout_t * elem = deq_fd_vote_lockout_t_push_tail_nocopy( out->lockouts );
    1267       34489 :     READ_U64( elem->slot, data, sz );
    1268       34489 :     READ_U32( elem->confirmation_count, data, sz );
    1269       34489 :   }
    1270             : 
    1271         846 :   READ_OPTION( out->has_root, data, sz );
    1272         842 :   if( out->has_root ) {
    1273         301 :     READ_U64( out->root, data, sz );
    1274         301 :   }
    1275         838 :   READ_HASH( out->hash, data, sz );
    1276         826 :   READ_OPTION( out->has_timestamp, data, sz );
    1277         821 :   if( out->has_timestamp ) {
    1278         382 :     READ_I64( out->timestamp, data, sz );
    1279         382 :   }
    1280         817 :   return 0;
    1281         821 : }
    1282             : 
    1283             : /* This function contains slightly custom deserialization logic to
    1284             :    adhere to serde_compact_vote_state_update in Agave.
    1285             :    https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v5.1.1/vote-interface/src/state/mod.rs#L265-L299 */
    1286             : static int
    1287             : deser_compact_vote_state_update( fd_compact_vote_state_update_t * out,
    1288             :                                  uchar const **                   data,
    1289         262 :                                  ulong *                          sz ) {
    1290         262 :   READ_U64( out->root, data, sz );
    1291             : 
    1292         261 :   ushort lockouts_len;
    1293         261 :   READ_COMPACT_U16( lockouts_len, data, sz );
    1294         253 :   CHECK( lockouts_len<=FD_VOTE_INSTR_MAX_LOCKOUT_OFFSETS_LEN );
    1295         233 :   out->lockouts_len = lockouts_len;
    1296             : 
    1297         233 :   out->lockouts = (fd_lockout_offset_t *)out->lockouts_mem;
    1298             : 
    1299         233 :   ulong slot = out->root!=ULONG_MAX ? out->root : 0UL;
    1300        5031 :   for( ushort i=0; i<lockouts_len; i++ ) {
    1301        4822 :     READ_U64_VARINT( out->lockouts[i].offset, data, sz );
    1302        4819 :     READ_U8( out->lockouts[i].confirmation_count, data, sz );
    1303             : 
    1304             :     /* Custom logic: check that slot+lockout_offset
    1305             :        does not overflow */
    1306        4818 :     CHECK( !__builtin_uaddl_overflow( slot, out->lockouts[i].offset, &slot ) );
    1307        4818 :   }
    1308             : 
    1309         209 :   READ_HASH( out->hash, data, sz );
    1310         208 :   READ_OPTION( out->has_timestamp, data, sz );
    1311         129 :   if( out->has_timestamp ) {
    1312          63 :     READ_I64( out->timestamp, data, sz );
    1313          63 :   }
    1314         127 :   return 0;
    1315         129 : }
    1316             : 
    1317             : /* Similar to above. Tower sync uses delta-encoded lockout offsets,
    1318             :    converting them to absolute slot numbers on the fly.  Mirrors the
    1319             :    checked arithmetic from Agave's custom deserializer:
    1320             :    https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v5.1.1/vote-interface/src/state/mod.rs#L360-L396 */
    1321             : 
    1322             : static int
    1323             : deser_tower_sync( fd_tower_sync_t * out,
    1324             :                   uchar const **    data,
    1325         825 :                   ulong *           sz ) {
    1326         825 :   READ_U64( out->root, data, sz );
    1327         824 :   out->has_root = 1;
    1328             : 
    1329             :   /* First bit of custom logic: if root is ULONG_MAX, set root to 0. */
    1330         824 :   if( FD_UNLIKELY( out->root==ULONG_MAX ) ) {
    1331         252 :     out->has_root = 0;
    1332         252 :     out->root     = 0UL;
    1333         252 :   }
    1334             : 
    1335         824 :   ushort lockout_offsets_len;
    1336         824 :   READ_COMPACT_U16( lockout_offsets_len, data, sz );
    1337         759 :   CHECK( lockout_offsets_len<=FD_VOTE_INSTR_MAX_LOCKOUT_OFFSETS_LEN );
    1338             : 
    1339         734 :   out->lockouts = deq_fd_vote_lockout_t_join( deq_fd_vote_lockout_t_new( out->lockouts_mem, FD_VOTE_INSTR_MAX_LOCKOUT_OFFSETS_LEN ) );
    1340             : 
    1341         734 :   ulong last_slot = out->root;
    1342       27783 :   for( ushort i=0; i<lockout_offsets_len; i++ ) {
    1343       27095 :     fd_vote_lockout_t * elem = deq_fd_vote_lockout_t_push_tail_nocopy( out->lockouts );
    1344             : 
    1345       27095 :     ulong offset;
    1346       27095 :     READ_U64_VARINT( offset, data, sz );
    1347             : 
    1348       27070 :     uchar confirmation_count;
    1349       27070 :     READ_U8( confirmation_count, data, sz );
    1350             : 
    1351             :     /* Second bit of custom logic: check that last_slot+offset does not
    1352             :        overflow */
    1353       27066 :     CHECK( !__builtin_uaddl_overflow( last_slot, offset, &elem->slot ) );
    1354       27049 :     elem->confirmation_count = (uint)confirmation_count;
    1355       27049 :     last_slot = elem->slot;
    1356       27049 :   }
    1357             : 
    1358         688 :   out->lockouts_cnt = (ulong)lockout_offsets_len;
    1359             : 
    1360         688 :   READ_HASH( out->hash, data, sz );
    1361         685 :   READ_OPTION( out->has_timestamp, data, sz );
    1362         683 :   if( out->has_timestamp ) {
    1363         482 :     READ_I64( out->timestamp, data, sz );
    1364         482 :   }
    1365         682 :   READ_HASH( out->block_id, data, sz );
    1366         681 :   return 0;
    1367         682 : }
    1368             : 
    1369             : static int
    1370             : deser_vote_authorize_with_seed( fd_vote_authorize_with_seed_args_t * out,
    1371             :                                 uchar const **                       data,
    1372         255 :                                 ulong *                              sz ) {
    1373         255 :   CHECK( !deser_vote_authorize( &out->authorization_type, data, sz ) );
    1374         248 :   READ_PUBKEY( out->current_authority_derived_key_owner, data, sz );
    1375             : 
    1376         243 :   ulong seed_len;
    1377         243 :   READ_U64( seed_len, data, sz );
    1378             :   /* https://github.com/anza-xyz/agave/blob/v4.2.0-beta.1/programs/vote/src/vote_processor.rs#L42-L49 */
    1379         242 :   CHECK( seed_len<=FD_VOTE_INSTR_SEED_MAX );
    1380         242 :   out->current_authority_derived_key_seed_len = seed_len;
    1381             : 
    1382         242 :   READ_BYTES( out->current_authority_derived_key_seed, seed_len, data, sz );
    1383         241 :   CHECK( fd_utf8_verify( (char const *)out->current_authority_derived_key_seed, seed_len ) );
    1384             : 
    1385         201 :   READ_PUBKEY( out->new_authority, data, sz );
    1386         200 :   return 0;
    1387         201 : }
    1388             : 
    1389             : static int
    1390             : deser_vote_authorize_checked_with_seed( fd_vote_authorize_checked_with_seed_args_t * out,
    1391             :                                         uchar const **                               data,
    1392         259 :                                         ulong *                                      sz ) {
    1393         259 :   CHECK( !deser_vote_authorize( &out->authorization_type, data, sz ) );
    1394         255 :   READ_PUBKEY( out->current_authority_derived_key_owner, data, sz );
    1395             : 
    1396         255 :   ulong seed_len;
    1397         255 :   READ_U64( seed_len, data, sz );
    1398             :   /* https://github.com/anza-xyz/agave/blob/v4.2.0-beta.1/programs/vote/src/vote_processor.rs#L130 */
    1399         254 :   CHECK( seed_len<=FD_VOTE_INSTR_SEED_MAX );
    1400         242 :   out->current_authority_derived_key_seed_len = seed_len;
    1401             : 
    1402         242 :   READ_BYTES( out->current_authority_derived_key_seed, seed_len, data, sz );
    1403         239 :   CHECK( fd_utf8_verify( (char const *)out->current_authority_derived_key_seed, seed_len ) );
    1404         174 :   return 0;
    1405         239 : }
    1406             : 
    1407             : /**********************************************************************/
    1408             : /* Vote instruction -- top-level decoder                              */
    1409             : /**********************************************************************/
    1410             : 
    1411             : /* FD_VOTE_INSTR_PREFIX_SZ covers the discriminant and every scalar
    1412             :    field of every union member.  The rest is deque / seed storage the
    1413             :    decoder fills up to the decoded count, which is all consumers read. */
    1414             : 
    1415        5276 : #define FD_VOTE_INSTR_PREFIX_SZ (320UL)
    1416             : 
    1417             : FD_STATIC_ASSERT( offsetof( fd_vote_instruction_t, vote.slots_mem                            )<=FD_VOTE_INSTR_PREFIX_SZ, vote_instr_prefix );
    1418             : FD_STATIC_ASSERT( offsetof( fd_vote_instruction_t, update_vote_state.lockouts_mem            )<=FD_VOTE_INSTR_PREFIX_SZ, vote_instr_prefix );
    1419             : FD_STATIC_ASSERT( offsetof( fd_vote_instruction_t, compact_update_vote_state.lockouts_mem    )<=FD_VOTE_INSTR_PREFIX_SZ, vote_instr_prefix );
    1420             : FD_STATIC_ASSERT( offsetof( fd_vote_instruction_t, tower_sync.lockouts_mem                   )<=FD_VOTE_INSTR_PREFIX_SZ, vote_instr_prefix );
    1421             : FD_STATIC_ASSERT( offsetof( fd_vote_instruction_t, authorize_with_seed.current_authority_derived_key_seed         )<=FD_VOTE_INSTR_PREFIX_SZ, vote_instr_prefix );
    1422             : FD_STATIC_ASSERT( offsetof( fd_vote_instruction_t, authorize_checked_with_seed.current_authority_derived_key_seed )<=FD_VOTE_INSTR_PREFIX_SZ, vote_instr_prefix );
    1423             : FD_STATIC_ASSERT( offsetof( fd_vote_instruction_t, initialize_account_v2 )+sizeof( fd_vote_init_v2_t )<=FD_VOTE_INSTR_PREFIX_SZ, vote_instr_prefix );
    1424             : FD_STATIC_ASSERT( sizeof( fd_vote_instruction_t )>=FD_VOTE_INSTR_PREFIX_SZ, vote_instr_prefix );
    1425             : 
    1426             : static int
    1427             : fd_vote_instruction_deserialize_inner( fd_vote_instruction_t * instruction,
    1428             :                                        uchar const *           data,
    1429        5276 :                                        ulong                   data_sz ) {
    1430             :   /* Zero the scalar prefix only; with handholding, poison the 10 KiB
    1431             :      tail so a read past the decoded count shows up. */
    1432        5276 :   fd_memset( instruction, 0, FD_VOTE_INSTR_PREFIX_SZ );
    1433             : #if FD_TMPL_USE_HANDHOLDING
    1434             :   fd_memset( (uchar *)instruction+FD_VOTE_INSTR_PREFIX_SZ, 0xA5, sizeof(fd_vote_instruction_t)-FD_VOTE_INSTR_PREFIX_SZ );
    1435             : #endif
    1436             : 
    1437        5276 :   uchar const ** p  = &data;
    1438        5276 :   ulong *        sz = &data_sz;
    1439             : 
    1440        5276 :   READ_U32( instruction->discriminant, p, sz );
    1441             : 
    1442        5261 :   switch( instruction->discriminant ) {
    1443             : 
    1444         233 :     case fd_vote_instruction_enum_initialize_account:
    1445         233 :       return deser_vote_init( &instruction->initialize_account, p, sz );
    1446             : 
    1447         325 :     case fd_vote_instruction_enum_authorize: {
    1448         325 :       READ_PUBKEY( instruction->authorize.pubkey, p, sz );
    1449         313 :       return deser_vote_authorize( &instruction->authorize.vote_authorize, p, sz );
    1450         325 :     }
    1451             : 
    1452         295 :     case fd_vote_instruction_enum_vote:
    1453         295 :       return deser_vote( &instruction->vote, p, sz );
    1454             : 
    1455         366 :     case fd_vote_instruction_enum_withdraw:
    1456         366 :       READ_U64( instruction->withdraw, p, sz );
    1457         365 :       return 0;
    1458             : 
    1459         402 :     case fd_vote_instruction_enum_update_validator_identity:
    1460         402 :       return 0;
    1461             : 
    1462         496 :     case fd_vote_instruction_enum_update_commission:
    1463         496 :       READ_U8( instruction->update_commission, p, sz );
    1464         494 :       return 0;
    1465             : 
    1466         276 :     case fd_vote_instruction_enum_vote_switch:
    1467         276 :       CHECK( !deser_vote( &instruction->vote_switch.vote, p, sz ) );
    1468         248 :       READ_HASH( instruction->vote_switch.hash, p, sz );
    1469         243 :       return 0;
    1470             : 
    1471         300 :     case fd_vote_instruction_enum_authorize_checked:
    1472         300 :       return deser_vote_authorize( &instruction->authorize_checked, p, sz );
    1473             : 
    1474         448 :     case fd_vote_instruction_enum_update_vote_state:
    1475         448 :       return deser_vote_state_update( &instruction->update_vote_state, p, sz );
    1476             : 
    1477         449 :     case fd_vote_instruction_enum_update_vote_state_switch:
    1478         449 :       CHECK( !deser_vote_state_update( &instruction->update_vote_state_switch.vote_state_update, p, sz ) );
    1479         403 :       READ_HASH( instruction->update_vote_state_switch.hash, p, sz );
    1480         392 :       return 0;
    1481             : 
    1482         255 :     case fd_vote_instruction_enum_authorize_with_seed:
    1483         255 :       return deser_vote_authorize_with_seed( &instruction->authorize_with_seed, p, sz );
    1484             : 
    1485         259 :     case fd_vote_instruction_enum_authorize_checked_with_seed:
    1486         259 :       return deser_vote_authorize_checked_with_seed( &instruction->authorize_checked_with_seed, p, sz );
    1487             : 
    1488         144 :     case fd_vote_instruction_enum_compact_update_vote_state:
    1489         144 :       return deser_compact_vote_state_update( &instruction->compact_update_vote_state, p, sz );
    1490             : 
    1491         118 :     case fd_vote_instruction_enum_compact_update_vote_state_switch:
    1492         118 :       CHECK( !deser_compact_vote_state_update( &instruction->compact_update_vote_state_switch.compact_vote_state_update, p, sz ) );
    1493          64 :       READ_HASH( instruction->compact_update_vote_state_switch.hash, p, sz );
    1494          62 :       return 0;
    1495             : 
    1496         543 :     case fd_vote_instruction_enum_tower_sync:
    1497         543 :       return deser_tower_sync( &instruction->tower_sync, p, sz );
    1498             : 
    1499         284 :     case fd_vote_instruction_enum_tower_sync_switch:
    1500         284 :       CHECK( !deser_tower_sync( &instruction->tower_sync_switch.tower_sync, p, sz ) );
    1501         199 :       READ_HASH( instruction->tower_sync_switch.hash, p, sz );
    1502         195 :       return 0;
    1503             : 
    1504           1 :     case fd_vote_instruction_enum_initialize_account_v2:
    1505           1 :       return deser_vote_init_v2( &instruction->initialize_account_v2, p, sz );
    1506             : 
    1507           0 :     case fd_vote_instruction_enum_update_commission_collector:
    1508           0 :       READ_ENUM( instruction->update_commission_collector.discriminant, 2U, p, sz );
    1509           0 :       return 0;
    1510             : 
    1511           0 :     case fd_vote_instruction_enum_update_commission_bps:
    1512           0 :       READ_U16( instruction->update_commission_bps.commission_bps, p, sz );
    1513           0 :       READ_ENUM( instruction->update_commission_bps.kind.discriminant, 2U, p, sz );
    1514           0 :       return 0;
    1515             : 
    1516           0 :     case fd_vote_instruction_enum_deposit_delegator_rewards:
    1517           0 :       READ_U64( instruction->deposit_delegator_rewards.deposit, p, sz );
    1518           0 :       return 0;
    1519             : 
    1520           0 :     case fd_vote_instruction_enum_update_location:
    1521           0 :       READ_I32( instruction->update_location.x, p, sz );
    1522           0 :       READ_I32( instruction->update_location.y, p, sz );
    1523           0 :       READ_I32( instruction->update_location.z, p, sz );
    1524           0 :       return 0;
    1525             : 
    1526          82 :     default:
    1527          82 :       return 1;
    1528        5261 :   }
    1529        5261 : }
    1530             : 
    1531             : fd_vote_instruction_t *
    1532             : fd_vote_instruction_deserialize( fd_vote_instruction_t * instruction,
    1533             :                                  uchar const *           data,
    1534        5276 :                                  ulong                   data_sz ) {
    1535             :   /* limited_deserialize(data, PACKET_DATA_SIZE)
    1536             :      https://github.com/anza-xyz/agave/blob/v4.2.0-beta.1/programs/vote/src/vote_processor.rs#L130 */
    1537        5276 :   data_sz = fd_ulong_min( data_sz, FD_TXN_MTU_V0 );
    1538        5276 :   CHECK_RET_NULL( !fd_vote_instruction_deserialize_inner( instruction, data, data_sz ) );
    1539        4534 :   return instruction;
    1540        5276 : }

Generated by: LCOV version 1.14