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

Generated by: LCOV version 1.14