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: 427 792 53.9 %
Date: 2026-06-29 05:51:35 Functions: 25 32 78.1 %

          Line data    Source code
       1             : #include "fd_vote_codec.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       89406 : #define CHECK( cond ) do {               \
      13       89406 :   if( FD_UNLIKELY( !(cond) ) ) return 1; \
      14       89406 : } while( 0 )
      15             : 
      16         312 : #define CHECK_RET_NULL( cond ) do {          \
      17         312 :   if( FD_UNLIKELY( !(cond) ) ) return NULL;  \
      18         312 : } while( 0 )
      19             : 
      20        1755 : #define CHECK_U64_MUL_OVERFLOW( a, b ) do {                \
      21        1755 :   ulong _dummy;                                            \
      22        1755 :   CHECK( !__builtin_umull_overflow( (a), (b), &_dummy ) ); \
      23        1755 : } while( 0 )
      24             : 
      25             : /**********************************************************************/
      26             : /* Read macros                                                        */
      27             : /**********************************************************************/
      28             : 
      29         213 : #define READ_BYTES( dst, n, data, sz ) do { \
      30         213 :   CHECK( (n)<=(*(sz)) );                    \
      31         213 :   fd_memcpy( (dst), *(data), (n) );         \
      32         213 :   *(data) += (n);                           \
      33         213 :   *(sz) -= (n);                             \
      34         213 : } while( 0 )
      35             : 
      36        2316 : #define SKIP_BYTES( n, data, sz ) do { \
      37        2316 :   CHECK( (n)<=(*(sz)) );               \
      38        2316 :   *(data) += (n);                      \
      39        2316 :   *(sz) -= (n);                        \
      40        2316 : } while( 0 )
      41             : 
      42         930 : #define READ_U8( dst, data, sz ) do { \
      43         930 :   CHECK( 1UL<=(*(sz)) );              \
      44         930 :   (dst) = FD_LOAD( uchar, *(data) );  \
      45         930 :   *(data) += 1UL;                     \
      46         930 :   *(sz) -= 1UL;                       \
      47         930 : } while( 0 )
      48             : 
      49          12 : #define READ_U16( dst, data, sz ) do {  \
      50          12 :   CHECK( 2UL<=(*(sz)) );                \
      51          12 :   (dst) = FD_LOAD( ushort, *(data) );   \
      52          12 :   *(data) += 2UL;                       \
      53          12 :   *(sz) -= 2UL;                         \
      54          12 : } while( 0 )
      55             : 
      56         897 : #define READ_U32( dst, data, sz ) do { \
      57         897 :   CHECK( 4UL<=(*(sz)) );               \
      58         897 :   (dst) = FD_LOAD( uint, *(data) );    \
      59         897 :   *(data) += 4UL;                      \
      60         897 :   *(sz) -= 4UL;                        \
      61         897 : } while( 0 )
      62             : 
      63        1920 : #define READ_U64( dst, data, sz ) do {  \
      64        1920 :   CHECK( 8UL<=(*(sz)) );                \
      65        1920 :   (dst) = FD_LOAD( ulong, *(data) );    \
      66        1920 :   *(data) += 8UL;                       \
      67        1920 :   *(sz) -= 8UL;                         \
      68        1920 : } while( 0 )
      69             : 
      70           0 : #define READ_I64( dst, data, sz ) do { \
      71           0 :   CHECK( 8UL<=(*(sz)) );               \
      72           0 :   (dst) = FD_LOAD( long, *(data) );    \
      73           0 :   *(data) += 8UL;                      \
      74           0 :   *(sz) -= 8UL;                        \
      75           0 : } while( 0 )
      76             : 
      77             : #define READ_HASH( dst, data, sz ) \
      78         129 :   READ_BYTES( (dst).uc, 32UL, data, sz )
      79             : 
      80         129 : #define READ_PUBKEY READ_HASH
      81             : 
      82          66 : #define READ_BOOL( dst, data, sz ) do { \
      83          66 :   READ_U8( dst, data, sz );             \
      84          66 :   CHECK( (dst)==0 || (dst)==1 );        \
      85          66 : } while( 0 )
      86             : 
      87          33 : #define READ_OPTION READ_BOOL
      88             : 
      89           0 : #define READ_ENUM( dst, n, data, sz ) do { \
      90           0 :   CHECK( 4UL<=(*(sz)) );                   \
      91           0 :   (dst) = FD_LOAD( uint, *(data) );        \
      92           0 :   CHECK( (dst)<(n) );                      \
      93           0 :   *(data) += 4UL;                          \
      94           0 :   *(sz) -= 4UL;                            \
      95           0 : } while( 0 )
      96             : 
      97             : /* Varint decoder for u64 (LEB128). */
      98           0 : #define READ_U64_VARINT( dst, data, sz ) do {                                  \
      99           0 :   ulong _val = 0UL;                                                            \
     100           0 :   uint  _shift = 0U;                                                           \
     101           0 :   for(;;) {                                                                    \
     102           0 :     CHECK( 1UL<=(*(sz)) );                                                     \
     103           0 :     uchar _byte = FD_LOAD( uchar, *(data) );                                   \
     104           0 :     *(data) += 1UL;                                                            \
     105           0 :     *(sz) -= 1UL;                                                              \
     106           0 :     _val |= (ulong)(_byte & 0x7F) << _shift;                                   \
     107           0 :     if( FD_LIKELY( !(_byte & 0x80) ) ) {                                       \
     108           0 :       CHECK( (_val>>_shift)==(ulong)_byte );     /* last byte not truncated */ \
     109           0 :       CHECK( _byte || !_shift );                 /* no trailing zero bytes */  \
     110           0 :       (dst) = _val;                                                            \
     111           0 :       break;                                                                   \
     112           0 :     }                                                                          \
     113           0 :     _shift += 7U;                                                              \
     114           0 :     CHECK( _shift<64U );                                                       \
     115           0 :   }                                                                            \
     116           0 : } while( 0 )
     117             : 
     118           0 : #define READ_COMPACT_U16( dst, data, sz ) do {      \
     119           0 :   ulong _n = fd_cu16_dec( *(data), *(sz), &(dst) ); \
     120           0 :   CHECK( _n );                                      \
     121           0 :   *(data) += _n;                                    \
     122           0 :   *(sz)   -= _n;                                    \
     123           0 : } while( 0 )
     124             : 
     125             : /**********************************************************************/
     126             : /* Write macros                                                       */
     127             : /**********************************************************************/
     128             : 
     129       18864 : #define WRITE_BYTES( src, n, out, out_sz ) do { \
     130       18864 :   CHECK( (n)<=(*(out_sz)) );                    \
     131       18864 :   fd_memcpy( *(out), (src), (n) );              \
     132       18864 :   *(out) += (n);                                \
     133       18864 :   *(out_sz) -= (n);                             \
     134       18864 : } while( 0 )
     135             : 
     136       11100 : #define WRITE_U8( val, out, out_sz ) do { \
     137       11100 :   CHECK( 1UL<=(*(out_sz)) );              \
     138       11100 :   FD_STORE( uchar, *(out), (val) );       \
     139       11100 :   *(out) += 1UL;                          \
     140       11100 :   *(out_sz) -= 1UL;                       \
     141       11100 : } while( 0 )
     142             : 
     143         192 : #define WRITE_U16( val, out, out_sz ) do { \
     144         192 :   CHECK( 2UL<=(*(out_sz)) );               \
     145         192 :   FD_STORE( ushort, *(out), (val) );       \
     146         192 :   *(out) += 2UL;                           \
     147         192 :   *(out_sz) -= 2UL;                        \
     148         192 : } while( 0 )
     149             : 
     150        3732 : #define WRITE_U32( val, out, out_sz ) do { \
     151        3732 :   CHECK( 4UL<=(*(out_sz)) );               \
     152        3732 :   FD_STORE( uint, *(out), (val) );         \
     153        3732 :   *(out) += 4UL;                           \
     154        3732 :   *(out_sz) -= 4UL;                        \
     155        3732 : } while( 0 )
     156             : 
     157       18660 : #define WRITE_U64( val, out, out_sz ) do { \
     158       18660 :   CHECK( 8UL<=(*(out_sz)) );               \
     159       18660 :   FD_STORE( ulong, *(out), (val) );        \
     160       18660 :   *(out) += 8UL;                           \
     161       18660 :   *(out_sz) -= 8UL;                        \
     162       18660 : } 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       11388 :   WRITE_BYTES( (src).uc, 32UL, out, out_sz )
     173             : 
     174             : #define WRITE_BOOL( val, out, out_sz ) \
     175        7464 :   WRITE_U8( (uchar)!!(val), out, out_sz )
     176             : 
     177          96 : #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          33 :              ulong *            rem ) {
     191          33 :   ulong votes_len;
     192          33 :   READ_U64( votes_len, ptr, rem );
     193          33 :   CHECK( votes_len<=MAX_LOCKOUT_HISTORY );
     194             : 
     195          33 :   for( ulong i=0UL; i<votes_len; i++ ) {
     196           0 :     fd_landed_vote_t * elem = deq_fd_landed_vote_t_push_tail_nocopy( votes );
     197           0 :     if( is_v1_14_11 ) {
     198           0 :       elem->latency = 0; /* Unused field for v1_14_11 */
     199           0 :     } else {
     200           0 :       READ_U8( elem->latency, ptr, rem );
     201           0 :     }
     202           0 :     READ_U64( elem->lockout.slot, ptr, rem );
     203           0 :     READ_U32( elem->lockout.confirmation_count, ptr, rem );
     204           0 :   }
     205          33 :   return 0;
     206          33 : }
     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          33 :                  ulong *         rem ) {
     214          33 :   uchar opt;
     215          33 :   READ_OPTION( opt, ptr, rem );
     216          33 :   *has_root_slot = opt;
     217          33 :   if( opt ) {
     218           0 :     READ_U64( *root_slot, ptr, rem );
     219           0 :   }
     220          33 :   return 0;
     221          33 : }
     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          33 :                          ulong *                             rem ) {
     229          33 :   ulong authorized_voters_len;
     230          33 :   READ_U64( authorized_voters_len, ptr, rem );
     231          33 :   CHECK( authorized_voters_len<=MAX_AUTHORIZED_VOTERS );
     232          66 :   for( ulong i=0UL; i<authorized_voters_len; i++ ) {
     233          33 :     fd_vote_authorized_voter_t * voter = fd_vote_authorized_voters_pool_ele_acquire( pool );
     234          33 :     READ_U64( voter->epoch, ptr, rem );
     235          33 :     READ_PUBKEY( voter->pubkey, ptr, rem );
     236          33 :     voter->prio = voter->pubkey.uc[0];
     237             : 
     238             :     /* Check for existing entries, overwrite if exists */
     239          33 :     fd_vote_authorized_voter_t * existing_voter = fd_vote_authorized_voters_treap_ele_query( treap, voter->epoch, pool );
     240          33 :     if( FD_UNLIKELY( existing_voter ) ) {
     241           0 :       fd_vote_authorized_voters_treap_ele_remove( treap, existing_voter, pool );
     242           0 :       fd_vote_authorized_voters_pool_ele_release( pool, existing_voter );
     243           0 :     }
     244             : 
     245          33 :     fd_vote_authorized_voters_treap_ele_insert( treap, voter, pool );
     246          33 :   }
     247          33 :   return 0;
     248          33 : }
     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          33 :                     ulong *                  rem ) {
     258          33 :   READ_BYTES( prior_voters->buf, PRIOR_VOTERS_MAX*sizeof(fd_vote_prior_voter_t), ptr, rem );
     259          33 :   READ_U64( prior_voters->idx, ptr, rem );
     260          33 :   READ_BOOL( prior_voters->is_empty, ptr, rem );
     261          33 :   return 0;
     262          33 : }
     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          33 :                      ulong *                   rem ) {
     269          33 :   ulong epoch_credits_len;
     270          33 :   READ_U64( epoch_credits_len, ptr, rem );
     271          33 :   CHECK( epoch_credits_len<=MAX_EPOCH_CREDITS_HISTORY );
     272          33 :   for( ulong i=0UL; i<epoch_credits_len; i++ ) {
     273           0 :     fd_vote_epoch_credits_t * elem = deq_fd_vote_epoch_credits_t_push_tail_nocopy( epoch_credits );
     274           0 :     READ_BYTES( elem, sizeof(fd_vote_epoch_credits_t), ptr, rem );
     275           0 :   }
     276          33 :   return 0;
     277          33 : }
     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        3732 :            ulong *                  out_sz ) {
     291        3732 :   if( FD_UNLIKELY( votes==NULL ) ) {
     292           0 :     WRITE_U64( 0UL, out, out_sz );
     293           0 :     return 0;
     294           0 :   }
     295             : 
     296        3732 :   ulong votes_len = deq_fd_landed_vote_t_cnt( votes );
     297        3732 :   WRITE_U64( votes_len, out, out_sz );
     298             : 
     299        3732 :   for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( votes );
     300        3732 :        !deq_fd_landed_vote_t_iter_done( votes, iter );
     301        3732 :        iter = deq_fd_landed_vote_t_iter_next( votes, iter ) ) {
     302           0 :     fd_landed_vote_t const * elem = deq_fd_landed_vote_t_iter_ele_const( votes, iter );
     303           0 :     if( !is_v1_14_11 ) {
     304           0 :       WRITE_U8( elem->latency, out, out_sz );
     305           0 :     }
     306           0 :     WRITE_U64( elem->lockout.slot, out, out_sz );
     307           0 :     WRITE_U32( elem->lockout.confirmation_count, out, out_sz );
     308           0 :   }
     309        3732 :   return 0;
     310        3732 : }
     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        3732 :                ulong *  out_sz ) {
     318        3732 :   WRITE_BOOL( has_root_slot, out, out_sz );
     319        3732 :   if( has_root_slot ) {
     320           0 :     WRITE_U64( root_slot, out, out_sz );
     321           0 :   }
     322        3732 :   return 0;
     323        3732 : }
     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        3732 :                        ulong *                                   out_sz ) {
     331        3732 :   if( FD_UNLIKELY( treap==NULL ) ) {
     332           0 :     WRITE_U64( 0UL, out, out_sz );
     333           0 :     return 0;
     334           0 :   }
     335             : 
     336        3732 :   ulong len = fd_vote_authorized_voters_treap_ele_cnt( treap );
     337        3732 :   WRITE_U64( len, out, out_sz );
     338             : 
     339        3732 :   for( fd_vote_authorized_voters_treap_fwd_iter_t iter = fd_vote_authorized_voters_treap_fwd_iter_init( treap, pool );
     340        7464 :        !fd_vote_authorized_voters_treap_fwd_iter_done( iter );
     341        3732 :        iter = fd_vote_authorized_voters_treap_fwd_iter_next( iter, pool ) ) {
     342        3732 :     fd_vote_authorized_voter_t const * voter = fd_vote_authorized_voters_treap_fwd_iter_ele_const( iter, pool );
     343        3732 :     WRITE_U64( voter->epoch, out, out_sz );
     344        3732 :     WRITE_PUBKEY( voter->pubkey, out, out_sz );
     345        3732 :   }
     346        3732 :   return 0;
     347        3732 : }
     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        3636 :                   ulong *                        out_sz ) {
     357        3636 :   WRITE_BYTES( prior_voters->buf, PRIOR_VOTERS_MAX*sizeof(fd_vote_prior_voter_t), out, out_sz );
     358        3636 :   WRITE_U64( prior_voters->idx, out, out_sz );
     359        3636 :   WRITE_BOOL( prior_voters->is_empty, out, out_sz );
     360        3636 :   return 0;
     361        3636 : }
     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        3732 :                    ulong *                         out_sz ) {
     368        3732 :   if( FD_UNLIKELY( epoch_credits==NULL ) ) {
     369           0 :     WRITE_U64( 0UL, out, out_sz );
     370           0 :     return 0;
     371           0 :   }
     372             : 
     373        3732 :   ulong len = deq_fd_vote_epoch_credits_t_cnt( epoch_credits );
     374        3732 :   WRITE_U64( len, out, out_sz );
     375             : 
     376        3732 :   for( deq_fd_vote_epoch_credits_t_iter_t iter = deq_fd_vote_epoch_credits_t_iter_init( epoch_credits );
     377        3765 :        !deq_fd_vote_epoch_credits_t_iter_done( epoch_credits, iter );
     378        3732 :        iter = deq_fd_vote_epoch_credits_t_iter_next( epoch_credits, iter ) ) {
     379          33 :     fd_vote_epoch_credits_t const * elem = deq_fd_vote_epoch_credits_t_iter_ele_const( epoch_credits, iter );
     380          33 :     WRITE_BYTES( elem, sizeof(fd_vote_epoch_credits_t), out, out_sz );
     381          33 :   }
     382        3732 :   return 0;
     383        3732 : }
     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         261 : #define WIRE_OFF_NODE_PUBKEY       (4UL)
     397         165 : #define WIRE_OFF_V1V3_COMMISSION   (68UL)  /* 4 + 32 + 32 */
     398             : #define WIRE_OFF_V4_COMMISSION_BPS (132UL) /* 4 + 32 + 32 + 32 + 32 */
     399             : 
     400             : /* Byte size of a Lockout on the wire: u64 slot + u32 confirmation_count */
     401           0 : #define WIRE_LOCKOUT_SZ     (12UL)
     402             : /* Byte size of a LandedVote on the wire: u8 latency + u64 slot + u32 confirmation_count */
     403           6 : #define WIRE_LANDED_VOTE_SZ (13UL)
     404             : /* Byte size of an authorized voter entry: u64 epoch + 32B pubkey */
     405           6 : #define WIRE_AUTH_VOTER_SZ  (40UL)
     406             : /* Byte size of prior_voters on the wire: 32 * 48B + 8B idx + 1B is_empty */
     407           0 : #define WIRE_PRIOR_VOTERS_SZ (PRIOR_VOTERS_MAX * 48UL + 8UL + 1UL)
     408             : 
     409             : /* Offset of the votes vector (v1/v3) or BLS option (v4) — first byte
     410             :    after the fixed-offset prefix. */
     411           0 : #define WIRE_VOTES_OFF_V1_V3 (69UL)   /* 4 + 32 + 32 + 1 */
     412          75 : #define WIRE_BLS_OFF_V4      (144UL)  /* 4 + 32 + 32 + 32 + 32 + 2 + 2 + 8 */
     413             : 
     414             : /**********************************************************************/
     415             : /* Vote account state -- direct field accessors                       */
     416             : /* These read fields directly from raw bincode-encoded vote account   */
     417             : /* data without full deserialization.                                  */
     418             : /**********************************************************************/
     419             : 
     420             : int
     421             : fd_vote_account_node_pubkey( uchar const *  data,
     422             :                              ulong          data_sz,
     423         261 :                              fd_pubkey_t *  out ) {
     424         261 :   CHECK( data_sz>=WIRE_OFF_NODE_PUBKEY+32UL );
     425         261 :   fd_memcpy( out, data+WIRE_OFF_NODE_PUBKEY, 32UL );
     426         261 :   return 0;
     427         261 : }
     428             : 
     429             : int
     430             : fd_vote_account_commission_bps( uchar const * data,
     431             :                                 ulong         data_sz,
     432             :                                 int           commission_rate_in_basis_points,
     433         261 :                                 ushort *      out ) {
     434         261 :   uchar const * ptr       = data;
     435         261 :   ulong         remaining = data_sz;
     436             : 
     437         261 :   uint discriminant;
     438         261 :   READ_U32( discriminant, &ptr, &remaining );
     439             : 
     440         261 :   switch( discriminant ) {
     441           0 :     case fd_vote_state_versioned_enum_v1_14_11: /* fallthrough */
     442         165 :     case fd_vote_state_versioned_enum_v3:
     443         165 :       CHECK( data_sz>WIRE_OFF_V1V3_COMMISSION );
     444         165 :       *out = data[ WIRE_OFF_V1V3_COMMISSION ] * 100U;
     445         165 :       return 0;
     446          96 :     case fd_vote_state_versioned_enum_v4:
     447          96 :       CHECK( data_sz>=WIRE_OFF_V4_COMMISSION_BPS+2UL );
     448          96 :       *out = FD_LOAD( ushort, data+WIRE_OFF_V4_COMMISSION_BPS );
     449             : 
     450             :       /* Round down to the nearest whole percentage if SIMD-0291 is not
     451             :          yet active. */
     452          96 :       if( !commission_rate_in_basis_points ) {
     453           0 :         *out = (*out / 100U) * 100U;
     454           0 :       }
     455          96 :       return 0;
     456           0 :     default:
     457           0 :       return 1;
     458         261 :   }
     459         261 : }
     460             : 
     461             : /* Seeks past variable-length fields to the start of epoch_credits.
     462             :    On success sets *out_ptr to the first entry and *out_cnt to the
     463             :    entry count.  Returns 0 on success, 1 on error. */
     464             : 
     465             : static int
     466             : seek_epoch_credits( uchar const *                    data,
     467             :                     ulong                            data_sz,
     468             :                     fd_vote_epoch_credits_t const ** out_ptr,
     469         585 :                     ulong *                          out_cnt ) {
     470         585 :   uchar const * ptr       = data;
     471         585 :   ulong         remaining = data_sz;
     472             : 
     473         585 :   uint discriminant;
     474         585 :   READ_U32( discriminant, &ptr, &remaining );
     475             : 
     476         585 :   switch( discriminant ) {
     477           0 :     case fd_vote_state_versioned_enum_v1_14_11: {
     478           0 :       SKIP_BYTES( WIRE_VOTES_OFF_V1_V3-4UL, &ptr, &remaining );
     479             : 
     480             :       /* Skip votes Vec<Lockout> */
     481           0 :       ulong votes_len;
     482           0 :       READ_U64( votes_len, &ptr, &remaining );
     483           0 :       CHECK_U64_MUL_OVERFLOW( votes_len, WIRE_LOCKOUT_SZ );
     484           0 :       SKIP_BYTES( votes_len*WIRE_LOCKOUT_SZ, &ptr, &remaining );
     485             : 
     486             :       /* Skip root_slot Option<u64> */
     487           0 :       uchar has_root_slot;
     488           0 :       READ_U8( has_root_slot, &ptr, &remaining );
     489           0 :       if( has_root_slot ) {
     490           0 :         SKIP_BYTES( 8UL, &ptr, &remaining );
     491           0 :       }
     492             : 
     493             :       /* Skip authorized_voters BTreeMap<u64, Pubkey> */
     494           0 :       ulong authorized_voters_len;
     495           0 :       READ_U64( authorized_voters_len, &ptr, &remaining );
     496           0 :       CHECK_U64_MUL_OVERFLOW( authorized_voters_len, WIRE_AUTH_VOTER_SZ );
     497           0 :       SKIP_BYTES( authorized_voters_len*WIRE_AUTH_VOTER_SZ, &ptr, &remaining );
     498             : 
     499             :       /* Skip prior_voters (fixed size) */
     500           0 :       SKIP_BYTES( WIRE_PRIOR_VOTERS_SZ, &ptr, &remaining );
     501           0 :       break;
     502           0 :     }
     503             : 
     504         339 :     case fd_vote_state_versioned_enum_v3: {
     505         339 :       SKIP_BYTES( WIRE_VOTES_OFF_V1_V3-4UL, &ptr, &remaining );
     506             : 
     507             :       /* Skip votes Vec<LandedVote> */
     508         339 :       ulong votes_len;
     509         339 :       READ_U64( votes_len, &ptr, &remaining );
     510         339 :       CHECK_U64_MUL_OVERFLOW( votes_len, WIRE_LANDED_VOTE_SZ );
     511         339 :       SKIP_BYTES( votes_len*WIRE_LANDED_VOTE_SZ, &ptr, &remaining );
     512             : 
     513             :       /* Skip root_slot Option<u64> */
     514         339 :       uchar has_root_slot;
     515         339 :       READ_U8( has_root_slot, &ptr, &remaining );
     516         339 :       if( has_root_slot ) {
     517           0 :         SKIP_BYTES( 8UL, &ptr, &remaining );
     518           0 :       }
     519             : 
     520             :       /* Skip authorized_voters BTreeMap<u64, Pubkey> */
     521         339 :       ulong authorized_voters_len;
     522         339 :       READ_U64( authorized_voters_len, &ptr, &remaining );
     523         339 :       CHECK_U64_MUL_OVERFLOW( authorized_voters_len, WIRE_AUTH_VOTER_SZ );
     524         339 :       SKIP_BYTES( authorized_voters_len*WIRE_AUTH_VOTER_SZ, &ptr, &remaining );
     525             : 
     526             :       /* Skip prior_voters (fixed size) */
     527         339 :       SKIP_BYTES( WIRE_PRIOR_VOTERS_SZ, &ptr, &remaining );
     528         339 :       break;
     529         339 :     }
     530             : 
     531         339 :     case fd_vote_state_versioned_enum_v4: {
     532         246 :       SKIP_BYTES( WIRE_BLS_OFF_V4-4UL, &ptr, &remaining );
     533             : 
     534             :       /* Skip Option<bls_pubkey_compressed> */
     535         246 :       uchar has_bls_pubkey;
     536         246 :       READ_U8( has_bls_pubkey, &ptr, &remaining );
     537         246 :       if( has_bls_pubkey ) {
     538         222 :         SKIP_BYTES( FD_BLS_PUBKEY_COMPRESSED_SZ, &ptr, &remaining );
     539         222 :       }
     540             : 
     541             :       /* Skip votes Vec<LandedVote> */
     542         246 :       ulong votes_len;
     543         246 :       READ_U64( votes_len, &ptr, &remaining );
     544         246 :       CHECK_U64_MUL_OVERFLOW( votes_len, WIRE_LANDED_VOTE_SZ );
     545         246 :       SKIP_BYTES( votes_len*WIRE_LANDED_VOTE_SZ, &ptr, &remaining );
     546             : 
     547             :       /* Skip root_slot Option<u64> */
     548         246 :       uchar has_root_slot;
     549         246 :       READ_U8( has_root_slot, &ptr, &remaining );
     550         246 :       if( has_root_slot ) {
     551           0 :         SKIP_BYTES( 8UL, &ptr, &remaining );
     552           0 :       }
     553             : 
     554             :       /* Skip authorized_voters BTreeMap<u64, Pubkey> */
     555         246 :       ulong authorized_voters_len;
     556         246 :       READ_U64( authorized_voters_len, &ptr, &remaining );
     557         246 :       CHECK_U64_MUL_OVERFLOW( authorized_voters_len, WIRE_AUTH_VOTER_SZ );
     558         246 :       SKIP_BYTES( authorized_voters_len*WIRE_AUTH_VOTER_SZ, &ptr, &remaining );
     559         246 :       break;
     560         246 :     }
     561             : 
     562         246 :     default:
     563           0 :       return 1;
     564         585 :   }
     565             : 
     566             :   /* Now at epoch_credits deque */
     567         585 :   ulong epoch_credits_len;
     568         585 :   READ_U64( epoch_credits_len, &ptr, &remaining );
     569         585 :   CHECK( epoch_credits_len<=MAX_EPOCH_CREDITS_HISTORY );
     570         585 :   CHECK_U64_MUL_OVERFLOW( epoch_credits_len, sizeof(fd_vote_epoch_credits_t) );
     571         585 :   CHECK( epoch_credits_len*sizeof(fd_vote_epoch_credits_t)<=remaining );
     572             : 
     573         585 :   *out_ptr = (fd_vote_epoch_credits_t const *)ptr;
     574         585 :   *out_cnt = epoch_credits_len;
     575         585 :   return 0;
     576         585 : }
     577             : 
     578             : int
     579             : fd_vote_account_last_timestamp( uchar const *             data,
     580             :                                 ulong                     data_sz,
     581         324 :                                 fd_vote_block_timestamp_t * out ) {
     582         324 :   fd_vote_epoch_credits_t const * epoch_credits_ptr;
     583         324 :   ulong                           epoch_credits_len;
     584         324 :   CHECK( !seek_epoch_credits( data, data_sz, &epoch_credits_ptr, &epoch_credits_len ) );
     585             : 
     586         324 :   uchar const * timestamp_ptr = (uchar const *)( epoch_credits_ptr + epoch_credits_len );
     587         324 :   CHECK( timestamp_ptr+sizeof(fd_vote_block_timestamp_t)<=data+data_sz );
     588             : 
     589         324 :   fd_memcpy( out, timestamp_ptr, sizeof(fd_vote_block_timestamp_t) );
     590         324 :   return 0;
     591         324 : }
     592             : 
     593             : int
     594             : fd_vote_account_is_v4_with_bls_pubkey( uchar const * data,
     595          69 :                                        ulong         data_sz ) {
     596          69 :   if( FD_UNLIKELY( data_sz<WIRE_BLS_OFF_V4+1UL ) ) return 0;
     597          69 :   uint discriminant = FD_LOAD( uint, data );
     598          69 :   if( discriminant!=fd_vote_state_versioned_enum_v4 ) return 0;
     599          69 :   return !!data[ WIRE_BLS_OFF_V4 ];
     600          69 : }
     601             : 
     602             : fd_vote_epoch_credits_t const *
     603             : fd_vote_account_epoch_credits( uchar const * data,
     604             :                                ulong         data_sz,
     605         261 :                                ulong *       cnt ) {
     606         261 :   fd_vote_epoch_credits_t const * ptr;
     607         261 :   CHECK_RET_NULL( !seek_epoch_credits( data, data_sz, &ptr, cnt ) );
     608         261 :   return ptr;
     609         261 : }
     610             : 
     611             : /**********************************************************************/
     612             : /* Vote account state -- public API                                   */
     613             : /**********************************************************************/
     614             : 
     615             : fd_vote_state_versioned_t *
     616             : fd_vote_state_versioned_new( fd_vote_state_versioned_t * self,
     617        3738 :                              uint                        kind ) {
     618        3738 :   if( FD_UNLIKELY( !self ) ) {
     619           0 :     FD_LOG_WARNING(( "NULL mem" ));
     620           0 :     return NULL;
     621           0 :   }
     622             : 
     623             :   /* Init and join data structures */
     624        3738 :   fd_landed_vote_t * votes = deq_fd_landed_vote_t_join(
     625        3738 :     deq_fd_landed_vote_t_new( self->landed_votes_mem, MAX_LOCKOUT_HISTORY_CAPACITY )
     626        3738 :   );
     627        3738 :   fd_vote_epoch_credits_t * epoch_credits = deq_fd_vote_epoch_credits_t_join(
     628        3738 :     deq_fd_vote_epoch_credits_t_new( self->epoch_credits_mem )
     629        3738 :   );
     630        3738 :   fd_vote_authorized_voter_t * authorized_voters_pool  = fd_vote_authorized_voters_pool_join(
     631        3738 :     fd_vote_authorized_voters_pool_new( self->authorized_voters_pool_mem, MAX_AUTHORIZED_VOTERS_CAPACITY )
     632        3738 :   );
     633        3738 :   fd_vote_authorized_voters_treap_t * authorized_voters_treap = fd_vote_authorized_voters_treap_join(
     634        3738 :     fd_vote_authorized_voters_treap_new( self->authorized_voters_treap_mem, MAX_AUTHORIZED_VOTERS_CAPACITY )
     635        3738 :   );
     636             : 
     637        3738 :   self->kind = kind;
     638        3738 :   switch( kind ) {
     639           6 :     case fd_vote_state_versioned_enum_uninitialized:
     640           6 :       break;
     641           0 :     case fd_vote_state_versioned_enum_v1_14_11:
     642           0 :       memset( &self->v1_14_11, 0, sizeof(fd_vote_state_1_14_11_t) );
     643           0 :       self->v1_14_11.votes                   = votes;
     644           0 :       self->v1_14_11.epoch_credits           = epoch_credits;
     645           0 :       self->v1_14_11.authorized_voters.pool  = authorized_voters_pool;
     646           0 :       self->v1_14_11.authorized_voters.treap = authorized_voters_treap;
     647           0 :       break;
     648        3636 :     case fd_vote_state_versioned_enum_v3:
     649        3636 :       memset( &self->v3, 0, sizeof(fd_vote_state_v3_t) );
     650        3636 :       self->v3.votes                   = votes;
     651        3636 :       self->v3.epoch_credits           = epoch_credits;
     652        3636 :       self->v3.authorized_voters.pool  = authorized_voters_pool;
     653        3636 :       self->v3.authorized_voters.treap = authorized_voters_treap;
     654        3636 :       break;
     655          96 :     case fd_vote_state_versioned_enum_v4:
     656          96 :       memset( &self->v4, 0, sizeof(fd_vote_state_v4_t) );
     657          96 :       self->v4.votes                    = votes;
     658          96 :       self->v4.epoch_credits            = epoch_credits;
     659          96 :       self->v4.authorized_voters.pool   = authorized_voters_pool;
     660          96 :       self->v4.authorized_voters.treap  = authorized_voters_treap;
     661          96 :       break;
     662           0 :     default:
     663           0 :       return NULL;
     664        3738 :   }
     665             : 
     666        3738 :   return self;
     667        3738 : }
     668             : 
     669             : static int
     670             : fd_vote_state_versioned_deserialize_inner( fd_vote_state_versioned_t * self,
     671             :                                            uchar const *               payload,
     672          39 :                                            ulong                       payload_sz ) {
     673          39 :   CHECK( self!=NULL );
     674          39 :   CHECK( payload!=NULL );
     675             : 
     676          39 :   uchar const * ptr = payload;
     677          39 :   ulong         rem = payload_sz;
     678             : 
     679          39 :   uint kind;
     680          39 :   READ_U32( kind, &ptr, &rem );
     681             : 
     682          39 :   CHECK( fd_vote_state_versioned_new( self, kind )!=NULL );
     683             : 
     684          39 :   switch( self->kind ) {
     685           6 :     case fd_vote_state_versioned_enum_uninitialized:
     686             :       /* No-op, nothing to decode */
     687           6 :       return 0;
     688             : 
     689           0 :     case fd_vote_state_versioned_enum_v1_14_11: {
     690           0 :       READ_PUBKEY( self->v1_14_11.node_pubkey, &ptr, &rem );
     691           0 :       READ_PUBKEY( self->v1_14_11.authorized_withdrawer, &ptr, &rem );
     692           0 :       READ_U8( self->v1_14_11.commission, &ptr, &rem );
     693           0 :       CHECK( !deser_votes( self->v1_14_11.votes, 1, &ptr, &rem ) ); /* v1_14_11 wire format is Lockout, not LandedVote */
     694           0 :       CHECK( !deser_root_slot( &self->v1_14_11.has_root_slot, &self->v1_14_11.root_slot, &ptr, &rem ) );
     695           0 :       CHECK( !deser_authorized_voters( self->v1_14_11.authorized_voters.pool, self->v1_14_11.authorized_voters.treap, &ptr, &rem ) );
     696           0 :       CHECK( !deser_prior_voters( &self->v1_14_11.prior_voters, &ptr, &rem ) );
     697           0 :       CHECK( !deser_epoch_credits( self->v1_14_11.epoch_credits, &ptr, &rem ) );
     698           0 :       READ_BYTES( &self->v1_14_11.last_timestamp, sizeof(fd_vote_block_timestamp_t), &ptr, &rem );
     699           0 :       break;
     700           0 :     }
     701             : 
     702          33 :     case fd_vote_state_versioned_enum_v3: {
     703          33 :       READ_PUBKEY( self->v3.node_pubkey, &ptr, &rem );
     704          33 :       READ_PUBKEY( self->v3.authorized_withdrawer, &ptr, &rem );
     705          33 :       READ_U8( self->v3.commission, &ptr, &rem );
     706          33 :       CHECK( !deser_votes( self->v3.votes, 0, &ptr, &rem ) );
     707          33 :       CHECK( !deser_root_slot( &self->v3.has_root_slot, &self->v3.root_slot, &ptr, &rem ) );
     708          33 :       CHECK( !deser_authorized_voters( self->v3.authorized_voters.pool, self->v3.authorized_voters.treap, &ptr, &rem ) );
     709          33 :       CHECK( !deser_prior_voters( &self->v3.prior_voters, &ptr, &rem ) );
     710          33 :       CHECK( !deser_epoch_credits( self->v3.epoch_credits, &ptr, &rem ) );
     711          33 :       READ_BYTES( &self->v3.last_timestamp, sizeof(fd_vote_block_timestamp_t), &ptr, &rem );
     712          33 :       break;
     713          33 :     }
     714             : 
     715          33 :     case fd_vote_state_versioned_enum_v4: {
     716           0 :       READ_PUBKEY( self->v4.node_pubkey, &ptr, &rem );
     717           0 :       READ_PUBKEY( self->v4.authorized_withdrawer, &ptr, &rem );
     718           0 :       READ_PUBKEY( self->v4.inflation_rewards_collector, &ptr, &rem );
     719           0 :       READ_PUBKEY( self->v4.block_revenue_collector, &ptr, &rem );
     720           0 :       READ_U16( self->v4.inflation_rewards_commission_bps, &ptr, &rem );
     721           0 :       READ_U16( self->v4.block_revenue_commission_bps, &ptr, &rem );
     722           0 :       READ_U64( self->v4.pending_delegator_rewards, &ptr, &rem );
     723             : 
     724             :       /* Option<[u8; 48]> */
     725           0 :       READ_OPTION( self->v4.has_bls_pubkey_compressed, &ptr, &rem );
     726           0 :       if( self->v4.has_bls_pubkey_compressed ) {
     727           0 :         READ_BYTES( self->v4.bls_pubkey_compressed, FD_BLS_PUBKEY_COMPRESSED_SZ, &ptr, &rem );
     728           0 :       }
     729             : 
     730           0 :       CHECK( !deser_votes( self->v4.votes, 0, &ptr, &rem ) );
     731           0 :       CHECK( !deser_root_slot( &self->v4.has_root_slot, &self->v4.root_slot, &ptr, &rem ) );
     732           0 :       CHECK( !deser_authorized_voters( self->v4.authorized_voters.pool, self->v4.authorized_voters.treap, &ptr, &rem ) );
     733           0 :       CHECK( !deser_epoch_credits( self->v4.epoch_credits, &ptr, &rem ) ); /* v4 has no prior_voters */
     734           0 :       READ_BYTES( &self->v4.last_timestamp, sizeof(fd_vote_block_timestamp_t), &ptr, &rem );
     735           0 :       break;
     736           0 :     }
     737             : 
     738           0 :     default:
     739           0 :       return 1;
     740          39 :   }
     741             : 
     742          33 :   return 0;
     743          39 : }
     744             : 
     745             : fd_vote_state_versioned_t *
     746             : fd_vote_state_versioned_deserialize( fd_vote_state_versioned_t * self,
     747             :                                      uchar const *               payload,
     748          39 :                                      ulong                       payload_sz ) {
     749          39 :   CHECK_RET_NULL( !fd_vote_state_versioned_deserialize_inner( self, payload, payload_sz ) );
     750          39 :   return self;
     751          39 : }
     752             : 
     753             : int
     754             : fd_vote_state_versioned_serialize( fd_vote_state_versioned_t const * self,
     755             :                                    uchar *                           buf,
     756        3732 :                                    ulong                             buf_sz ) {
     757        3732 :   CHECK( self!=NULL );
     758        3732 :   CHECK( buf!=NULL );
     759             : 
     760        3732 :   uchar * out    = buf;
     761        3732 :   ulong   out_sz = buf_sz;
     762             : 
     763        3732 :   WRITE_U32( self->kind, &out, &out_sz );
     764             : 
     765        3732 :   switch( self->kind ) {
     766           0 :     case fd_vote_state_versioned_enum_uninitialized:
     767             :       /* No-op, nothing to encode */
     768           0 :       return 0;
     769             : 
     770           0 :     case fd_vote_state_versioned_enum_v1_14_11: {
     771           0 :       WRITE_PUBKEY( self->v1_14_11.node_pubkey, &out, &out_sz );
     772           0 :       WRITE_PUBKEY( self->v1_14_11.authorized_withdrawer, &out, &out_sz );
     773           0 :       WRITE_U8( self->v1_14_11.commission, &out, &out_sz );
     774           0 :       CHECK( !ser_votes( self->v1_14_11.votes, 1, &out, &out_sz ) ); /* v1_14_11 wire format is Lockout, not LandedVote */
     775           0 :       CHECK( !ser_root_slot( self->v1_14_11.has_root_slot, self->v1_14_11.root_slot, &out, &out_sz ) );
     776           0 :       CHECK( !ser_authorized_voters( self->v1_14_11.authorized_voters.pool, self->v1_14_11.authorized_voters.treap, &out, &out_sz ) );
     777           0 :       CHECK( !ser_prior_voters( &self->v1_14_11.prior_voters, &out, &out_sz ) );
     778           0 :       CHECK( !ser_epoch_credits( self->v1_14_11.epoch_credits, &out, &out_sz ) );
     779           0 :       WRITE_BYTES( &self->v1_14_11.last_timestamp, sizeof(fd_vote_block_timestamp_t), &out, &out_sz );
     780           0 :       break;
     781           0 :     }
     782             : 
     783        3636 :     case fd_vote_state_versioned_enum_v3: {
     784        3636 :       WRITE_PUBKEY( self->v3.node_pubkey, &out, &out_sz );
     785        3636 :       WRITE_PUBKEY( self->v3.authorized_withdrawer, &out, &out_sz );
     786        3636 :       WRITE_U8( self->v3.commission, &out, &out_sz );
     787        3636 :       CHECK( !ser_votes( self->v3.votes, 0, &out, &out_sz ) );
     788        3636 :       CHECK( !ser_root_slot( self->v3.has_root_slot, self->v3.root_slot, &out, &out_sz ) );
     789        3636 :       CHECK( !ser_authorized_voters( self->v3.authorized_voters.pool, self->v3.authorized_voters.treap, &out, &out_sz ) );
     790        3636 :       CHECK( !ser_prior_voters( &self->v3.prior_voters, &out, &out_sz ) );
     791        3636 :       CHECK( !ser_epoch_credits( self->v3.epoch_credits, &out, &out_sz ) );
     792        3636 :       WRITE_BYTES( &self->v3.last_timestamp, sizeof(fd_vote_block_timestamp_t), &out, &out_sz );
     793        3636 :       break;
     794        3636 :     }
     795             : 
     796        3636 :     case fd_vote_state_versioned_enum_v4: {
     797          96 :       WRITE_PUBKEY( self->v4.node_pubkey, &out, &out_sz );
     798          96 :       WRITE_PUBKEY( self->v4.authorized_withdrawer, &out, &out_sz );
     799          96 :       WRITE_PUBKEY( self->v4.inflation_rewards_collector, &out, &out_sz );
     800          96 :       WRITE_PUBKEY( self->v4.block_revenue_collector, &out, &out_sz );
     801          96 :       WRITE_U16( self->v4.inflation_rewards_commission_bps, &out, &out_sz );
     802          96 :       WRITE_U16( self->v4.block_revenue_commission_bps, &out, &out_sz );
     803          96 :       WRITE_U64( self->v4.pending_delegator_rewards, &out, &out_sz );
     804             : 
     805             :       /* Option<[u8; 48]> */
     806          96 :       WRITE_OPTION( self->v4.has_bls_pubkey_compressed, &out, &out_sz );
     807          96 :       if( self->v4.has_bls_pubkey_compressed ) {
     808          75 :         WRITE_BYTES( self->v4.bls_pubkey_compressed, FD_BLS_PUBKEY_COMPRESSED_SZ, &out, &out_sz );
     809          75 :       }
     810             : 
     811          96 :       CHECK( !ser_votes( self->v4.votes, 0, &out, &out_sz ) );
     812          96 :       CHECK( !ser_root_slot( self->v4.has_root_slot, self->v4.root_slot, &out, &out_sz ) );
     813          96 :       CHECK( !ser_authorized_voters( self->v4.authorized_voters.pool, self->v4.authorized_voters.treap, &out, &out_sz ) );
     814          96 :       CHECK( !ser_epoch_credits( self->v4.epoch_credits, &out, &out_sz ) ); /* v4 has no prior_voters */
     815          96 :       WRITE_BYTES( &self->v4.last_timestamp, sizeof(fd_vote_block_timestamp_t), &out, &out_sz );
     816          96 :       break;
     817          96 :     }
     818             : 
     819          96 :     default:
     820           0 :       return 1;
     821        3732 :   }
     822             : 
     823        3732 :   return 0;
     824        3732 : }
     825             : 
     826             : ulong
     827           6 : fd_vote_state_versioned_serialized_size( fd_vote_state_versioned_t const * self ) {
     828           6 :   switch( self->kind ) {
     829             : 
     830           0 :     case fd_vote_state_versioned_enum_uninitialized:
     831           0 :       return 4UL;
     832             : 
     833           0 :     case fd_vote_state_versioned_enum_v1_14_11: {
     834           0 :       ulong votes_cnt         = self->v1_14_11.votes                   ? deq_fd_landed_vote_t_cnt( self->v1_14_11.votes )                                  : 0UL;
     835           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;
     836           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;
     837           0 :       return WIRE_VOTES_OFF_V1_V3
     838           0 :            + 8UL + votes_cnt * WIRE_LOCKOUT_SZ
     839           0 :            + 1UL + (ulong)self->v1_14_11.has_root_slot * 8UL
     840           0 :            + 8UL + auth_voters_cnt * WIRE_AUTH_VOTER_SZ
     841           0 :            + WIRE_PRIOR_VOTERS_SZ
     842           0 :            + 8UL + epoch_credits_cnt * sizeof(fd_vote_epoch_credits_t)
     843           0 :            + sizeof(fd_vote_block_timestamp_t);
     844           0 :     }
     845             : 
     846           0 :     case fd_vote_state_versioned_enum_v3: {
     847           0 :       ulong votes_cnt         = self->v3.votes                   ? deq_fd_landed_vote_t_cnt( self->v3.votes )                                  : 0UL;
     848           0 :       ulong auth_voters_cnt   = self->v3.authorized_voters.treap ? fd_vote_authorized_voters_treap_ele_cnt( self->v3.authorized_voters.treap )  : 0UL;
     849           0 :       ulong epoch_credits_cnt = self->v3.epoch_credits           ? deq_fd_vote_epoch_credits_t_cnt( self->v3.epoch_credits )                    : 0UL;
     850           0 :       return WIRE_VOTES_OFF_V1_V3
     851           0 :            + 8UL + votes_cnt * WIRE_LANDED_VOTE_SZ
     852           0 :            + 1UL + (ulong)self->v3.has_root_slot * 8UL
     853           0 :            + 8UL + auth_voters_cnt * WIRE_AUTH_VOTER_SZ
     854           0 :            + WIRE_PRIOR_VOTERS_SZ
     855           0 :            + 8UL + epoch_credits_cnt * sizeof(fd_vote_epoch_credits_t)
     856           0 :            + sizeof(fd_vote_block_timestamp_t);
     857           0 :     }
     858             : 
     859           6 :     case fd_vote_state_versioned_enum_v4: {
     860           6 :       ulong votes_cnt         = self->v4.votes                   ? deq_fd_landed_vote_t_cnt( self->v4.votes )                                  : 0UL;
     861           6 :       ulong auth_voters_cnt   = self->v4.authorized_voters.treap ? fd_vote_authorized_voters_treap_ele_cnt( self->v4.authorized_voters.treap )  : 0UL;
     862           6 :       ulong epoch_credits_cnt = self->v4.epoch_credits           ? deq_fd_vote_epoch_credits_t_cnt( self->v4.epoch_credits )                    : 0UL;
     863           6 :       return WIRE_BLS_OFF_V4
     864           6 :            + 1UL + (ulong)self->v4.has_bls_pubkey_compressed * FD_BLS_PUBKEY_COMPRESSED_SZ
     865           6 :            + 8UL + votes_cnt * WIRE_LANDED_VOTE_SZ
     866           6 :            + 1UL + (ulong)self->v4.has_root_slot * 8UL
     867           6 :            + 8UL + auth_voters_cnt * WIRE_AUTH_VOTER_SZ
     868           6 :            + 8UL + epoch_credits_cnt * sizeof(fd_vote_epoch_credits_t)
     869           6 :            + sizeof(fd_vote_block_timestamp_t);
     870           0 :     }
     871             : 
     872           0 :     default:
     873           0 :       return 0UL;
     874           6 :   }
     875           6 : }
     876             : 
     877             : /**********************************************************************/
     878             : /* Vote instruction -- per-variant deserializers                      */
     879             : /**********************************************************************/
     880             : 
     881             : static int
     882             : deser_vote_authorize( fd_vote_authorize_t * out,
     883             :                       uchar const **        data,
     884           0 :                       ulong *               sz ) {
     885           0 :   READ_ENUM( out->discriminant, 3U, data, sz );
     886           0 :   if( out->discriminant==fd_vote_authorize_enum_voter_with_bls ) {
     887           0 :     READ_BYTES( out->voter_with_bls.bls_pubkey, FD_BLS_PUBKEY_COMPRESSED_SZ, data, sz );
     888           0 :     READ_BYTES( out->voter_with_bls.bls_proof_of_possession, FD_BLS_PROOF_OF_POSSESSION_COMPRESSED_SZ, data, sz );
     889           0 :   }
     890           0 :   return 0;
     891           0 : }
     892             : 
     893             : static int
     894             : deser_vote_init( fd_vote_init_t * out,
     895             :                  uchar const **   data,
     896           6 :                  ulong *          sz ) {
     897           6 :   READ_BYTES( out, sizeof(fd_vote_init_t), data, sz );
     898           6 :   return 0;
     899           6 : }
     900             : 
     901             : static int
     902             : deser_vote_init_v2( fd_vote_init_v2_t * out,
     903             :                     uchar const **      data,
     904           6 :                     ulong *             sz ) {
     905           6 :   READ_PUBKEY( out->node_pubkey, data, sz );
     906           6 :   READ_PUBKEY( out->authorized_voter, data, sz );
     907           6 :   READ_BYTES( out->authorized_voter_bls_pubkey, FD_BLS_PUBKEY_COMPRESSED_SZ, data, sz );
     908           6 :   READ_BYTES( out->authorized_voter_bls_proof_of_possession, FD_BLS_PROOF_OF_POSSESSION_COMPRESSED_SZ, data, sz );
     909           6 :   READ_PUBKEY( out->authorized_withdrawer, data, sz );
     910           6 :   READ_U16( out->inflation_rewards_commission_bps, data, sz );
     911           6 :   READ_PUBKEY( out->inflation_rewards_collector, data, sz );
     912           6 :   READ_U16( out->block_revenue_commission_bps, data, sz );
     913           6 :   READ_PUBKEY( out->block_revenue_collector, data, sz );
     914           6 :   return 0;
     915           6 : }
     916             : 
     917             : static int
     918             : deser_vote( fd_vote_t *     out,
     919             :             uchar const **  data,
     920           0 :             ulong *         sz ) {
     921           0 :   ulong slots_len;
     922           0 :   READ_U64( slots_len, data, sz );
     923           0 :   CHECK( slots_len<=FD_VOTE_INSTR_MAX_SLOT_NUMS_LEN );
     924             : 
     925           0 :   out->slots = deq_ulong_join( deq_ulong_new( out->slots_mem, FD_VOTE_INSTR_MAX_SLOT_NUMS_LEN ) );
     926             : 
     927           0 :   for( ulong i=0UL; i<slots_len; i++ ) {
     928           0 :     ulong * elem = deq_ulong_push_tail_nocopy( out->slots );
     929           0 :     READ_U64( *elem, data, sz );
     930           0 :   }
     931             : 
     932           0 :   READ_HASH( out->hash, data, sz );
     933           0 :   READ_OPTION( out->has_timestamp, data, sz );
     934           0 :   if( out->has_timestamp ) {
     935           0 :     READ_I64( out->timestamp, data, sz );
     936           0 :   }
     937           0 :   return 0;
     938           0 : }
     939             : 
     940             : static int
     941             : deser_vote_state_update( fd_vote_state_update_t * out,
     942             :                          uchar const **           data,
     943           0 :                          ulong *                  sz ) {
     944           0 :   ulong lockouts_len;
     945           0 :   READ_U64( lockouts_len, data, sz );
     946           0 :   CHECK( lockouts_len<=FD_VOTE_INSTR_MAX_LOCKOUTS_LEN );
     947             : 
     948           0 :   out->lockouts = deq_fd_vote_lockout_t_join( deq_fd_vote_lockout_t_new( out->lockouts_mem, FD_VOTE_INSTR_MAX_LOCKOUTS_LEN ) );
     949             : 
     950           0 :   for( ulong i=0; i<lockouts_len; i++ ) {
     951           0 :     fd_vote_lockout_t * elem = deq_fd_vote_lockout_t_push_tail_nocopy( out->lockouts );
     952           0 :     READ_U64( elem->slot, data, sz );
     953           0 :     READ_U32( elem->confirmation_count, data, sz );
     954           0 :   }
     955             : 
     956           0 :   READ_OPTION( out->has_root, data, sz );
     957           0 :   if( out->has_root ) {
     958           0 :     READ_U64( out->root, data, sz );
     959           0 :   }
     960           0 :   READ_HASH( out->hash, data, sz );
     961           0 :   READ_OPTION( out->has_timestamp, data, sz );
     962           0 :   if( out->has_timestamp ) {
     963           0 :     READ_I64( out->timestamp, data, sz );
     964           0 :   }
     965           0 :   return 0;
     966           0 : }
     967             : 
     968             : /* This function contains slightly custom deserialization logic to
     969             :    adhere to serde_compact_vote_state_update in Agave.
     970             :    https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v5.1.1/vote-interface/src/state/mod.rs#L265-L299 */
     971             : static int
     972             : deser_compact_vote_state_update( fd_compact_vote_state_update_t * out,
     973             :                                  uchar const **                   data,
     974           0 :                                  ulong *                          sz ) {
     975           0 :   READ_U64( out->root, data, sz );
     976             : 
     977           0 :   ushort lockouts_len;
     978           0 :   READ_COMPACT_U16( lockouts_len, data, sz );
     979           0 :   CHECK( lockouts_len<=FD_VOTE_INSTR_MAX_LOCKOUT_OFFSETS_LEN );
     980           0 :   out->lockouts_len = lockouts_len;
     981             : 
     982           0 :   out->lockouts = (fd_lockout_offset_t *)out->lockouts_mem;
     983             : 
     984           0 :   ulong slot = out->root!=ULONG_MAX ? out->root : 0UL;
     985           0 :   for( ushort i=0; i<lockouts_len; i++ ) {
     986           0 :     READ_U64_VARINT( out->lockouts[i].offset, data, sz );
     987           0 :     READ_U8( out->lockouts[i].confirmation_count, data, sz );
     988             : 
     989             :     /* Custom logic: check that slot+lockout_offset
     990             :        does not overflow */
     991           0 :     CHECK( !__builtin_uaddl_overflow( slot, out->lockouts[i].offset, &slot ) );
     992           0 :   }
     993             : 
     994           0 :   READ_HASH( out->hash, data, sz );
     995           0 :   READ_OPTION( out->has_timestamp, data, sz );
     996           0 :   if( out->has_timestamp ) {
     997           0 :     READ_I64( out->timestamp, data, sz );
     998           0 :   }
     999           0 :   return 0;
    1000           0 : }
    1001             : 
    1002             : /* Similar to above. Tower sync uses delta-encoded lockout offsets,
    1003             :    converting them to absolute slot numbers on the fly.  Mirrors the
    1004             :    checked arithmetic from Agave's custom deserializer:
    1005             :    https://github.com/anza-xyz/solana-sdk/blob/vote-interface%40v5.1.1/vote-interface/src/state/mod.rs#L360-L396 */
    1006             : 
    1007             : static int
    1008             : deser_tower_sync( fd_tower_sync_t * out,
    1009             :                   uchar const **    data,
    1010           0 :                   ulong *           sz ) {
    1011           0 :   READ_U64( out->root, data, sz );
    1012           0 :   out->has_root = 1;
    1013             : 
    1014             :   /* First bit of custom logic: if root is ULONG_MAX, set root to 0. */
    1015           0 :   if( FD_UNLIKELY( out->root==ULONG_MAX ) ) {
    1016           0 :     out->has_root = 0;
    1017           0 :     out->root     = 0UL;
    1018           0 :   }
    1019             : 
    1020           0 :   ushort lockout_offsets_len;
    1021           0 :   READ_COMPACT_U16( lockout_offsets_len, data, sz );
    1022           0 :   CHECK( lockout_offsets_len<=FD_VOTE_INSTR_MAX_LOCKOUT_OFFSETS_LEN );
    1023             : 
    1024           0 :   out->lockouts = deq_fd_vote_lockout_t_join( deq_fd_vote_lockout_t_new( out->lockouts_mem, FD_VOTE_INSTR_MAX_LOCKOUT_OFFSETS_LEN ) );
    1025             : 
    1026           0 :   ulong last_slot = out->root;
    1027           0 :   for( ushort i=0; i<lockout_offsets_len; i++ ) {
    1028           0 :     fd_vote_lockout_t * elem = deq_fd_vote_lockout_t_push_tail_nocopy( out->lockouts );
    1029             : 
    1030           0 :     ulong offset;
    1031           0 :     READ_U64_VARINT( offset, data, sz );
    1032             : 
    1033           0 :     uchar confirmation_count;
    1034           0 :     READ_U8( confirmation_count, data, sz );
    1035             : 
    1036             :     /* Second bit of custom logic: check that last_slot+offset does not
    1037             :        overflow */
    1038           0 :     CHECK( !__builtin_uaddl_overflow( last_slot, offset, &elem->slot ) );
    1039           0 :     elem->confirmation_count = (uint)confirmation_count;
    1040           0 :     last_slot = elem->slot;
    1041           0 :   }
    1042             : 
    1043           0 :   out->lockouts_cnt = (ulong)lockout_offsets_len;
    1044             : 
    1045           0 :   READ_HASH( out->hash, data, sz );
    1046           0 :   READ_OPTION( out->has_timestamp, data, sz );
    1047           0 :   if( out->has_timestamp ) {
    1048           0 :     READ_I64( out->timestamp, data, sz );
    1049           0 :   }
    1050           0 :   READ_HASH( out->block_id, data, sz );
    1051           0 :   return 0;
    1052           0 : }
    1053             : 
    1054             : static int
    1055             : deser_vote_authorize_with_seed( fd_vote_authorize_with_seed_args_t * out,
    1056             :                                 uchar const **                       data,
    1057           0 :                                 ulong *                              sz ) {
    1058           0 :   CHECK( !deser_vote_authorize( &out->authorization_type, data, sz ) );
    1059           0 :   READ_PUBKEY( out->current_authority_derived_key_owner, data, sz );
    1060             : 
    1061           0 :   ulong seed_len;
    1062           0 :   READ_U64( seed_len, data, sz );
    1063           0 :   CHECK( seed_len<=FD_TXN_MTU );
    1064           0 :   out->current_authority_derived_key_seed_len = seed_len;
    1065             : 
    1066           0 :   READ_BYTES( out->current_authority_derived_key_seed, seed_len, data, sz );
    1067           0 :   CHECK( fd_utf8_verify( (char const *)out->current_authority_derived_key_seed, seed_len ) );
    1068             : 
    1069           0 :   READ_PUBKEY( out->new_authority, data, sz );
    1070           0 :   return 0;
    1071           0 : }
    1072             : 
    1073             : static int
    1074             : deser_vote_authorize_checked_with_seed( fd_vote_authorize_checked_with_seed_args_t * out,
    1075             :                                         uchar const **                               data,
    1076           0 :                                         ulong *                                      sz ) {
    1077           0 :   CHECK( !deser_vote_authorize( &out->authorization_type, data, sz ) );
    1078           0 :   READ_PUBKEY( out->current_authority_derived_key_owner, data, sz );
    1079             : 
    1080           0 :   ulong seed_len;
    1081           0 :   READ_U64( seed_len, data, sz );
    1082           0 :   CHECK( seed_len<=FD_TXN_MTU );
    1083           0 :   out->current_authority_derived_key_seed_len = seed_len;
    1084             : 
    1085           0 :   READ_BYTES( out->current_authority_derived_key_seed, seed_len, data, sz );
    1086           0 :   CHECK( fd_utf8_verify( (char const *)out->current_authority_derived_key_seed, seed_len ) );
    1087           0 :   return 0;
    1088           0 : }
    1089             : 
    1090             : /**********************************************************************/
    1091             : /* Vote instruction -- top-level decoder                              */
    1092             : /**********************************************************************/
    1093             : 
    1094             : static int
    1095             : fd_vote_instruction_deserialize_inner( fd_vote_instruction_t * instruction,
    1096             :                                        uchar const *           data,
    1097          12 :                                        ulong                   data_sz ) {
    1098          12 :   fd_memset( instruction, 0, sizeof(fd_vote_instruction_t) );
    1099             : 
    1100          12 :   uchar const ** p  = &data;
    1101          12 :   ulong *        sz = &data_sz;
    1102             : 
    1103          12 :   READ_U32( instruction->discriminant, p, sz );
    1104             : 
    1105          12 :   switch( instruction->discriminant ) {
    1106             : 
    1107           6 :     case fd_vote_instruction_enum_initialize_account:
    1108           6 :       return deser_vote_init( &instruction->initialize_account, p, sz );
    1109             : 
    1110           0 :     case fd_vote_instruction_enum_authorize: {
    1111           0 :       READ_PUBKEY( instruction->authorize.pubkey, p, sz );
    1112           0 :       return deser_vote_authorize( &instruction->authorize.vote_authorize, p, sz );
    1113           0 :     }
    1114             : 
    1115           0 :     case fd_vote_instruction_enum_vote:
    1116           0 :       return deser_vote( &instruction->vote, p, sz );
    1117             : 
    1118           0 :     case fd_vote_instruction_enum_withdraw:
    1119           0 :       READ_U64( instruction->withdraw, p, sz );
    1120           0 :       return 0;
    1121             : 
    1122           0 :     case fd_vote_instruction_enum_update_validator_identity:
    1123           0 :       return 0;
    1124             : 
    1125           0 :     case fd_vote_instruction_enum_update_commission:
    1126           0 :       READ_U8( instruction->update_commission, p, sz );
    1127           0 :       return 0;
    1128             : 
    1129           0 :     case fd_vote_instruction_enum_vote_switch:
    1130           0 :       CHECK( !deser_vote( &instruction->vote_switch.vote, p, sz ) );
    1131           0 :       READ_HASH( instruction->vote_switch.hash, p, sz );
    1132           0 :       return 0;
    1133             : 
    1134           0 :     case fd_vote_instruction_enum_authorize_checked:
    1135           0 :       return deser_vote_authorize( &instruction->authorize_checked, p, sz );
    1136             : 
    1137           0 :     case fd_vote_instruction_enum_update_vote_state:
    1138           0 :       return deser_vote_state_update( &instruction->update_vote_state, p, sz );
    1139             : 
    1140           0 :     case fd_vote_instruction_enum_update_vote_state_switch:
    1141           0 :       CHECK( !deser_vote_state_update( &instruction->update_vote_state_switch.vote_state_update, p, sz ) );
    1142           0 :       READ_HASH( instruction->update_vote_state_switch.hash, p, sz );
    1143           0 :       return 0;
    1144             : 
    1145           0 :     case fd_vote_instruction_enum_authorize_with_seed:
    1146           0 :       return deser_vote_authorize_with_seed( &instruction->authorize_with_seed, p, sz );
    1147             : 
    1148           0 :     case fd_vote_instruction_enum_authorize_checked_with_seed:
    1149           0 :       return deser_vote_authorize_checked_with_seed( &instruction->authorize_checked_with_seed, p, sz );
    1150             : 
    1151           0 :     case fd_vote_instruction_enum_compact_update_vote_state:
    1152           0 :       return deser_compact_vote_state_update( &instruction->compact_update_vote_state, p, sz );
    1153             : 
    1154           0 :     case fd_vote_instruction_enum_compact_update_vote_state_switch:
    1155           0 :       CHECK( !deser_compact_vote_state_update( &instruction->compact_update_vote_state_switch.compact_vote_state_update, p, sz ) );
    1156           0 :       READ_HASH( instruction->compact_update_vote_state_switch.hash, p, sz );
    1157           0 :       return 0;
    1158             : 
    1159           0 :     case fd_vote_instruction_enum_tower_sync:
    1160           0 :       return deser_tower_sync( &instruction->tower_sync, p, sz );
    1161             : 
    1162           0 :     case fd_vote_instruction_enum_tower_sync_switch:
    1163           0 :       CHECK( !deser_tower_sync( &instruction->tower_sync_switch.tower_sync, p, sz ) );
    1164           0 :       READ_HASH( instruction->tower_sync_switch.hash, p, sz );
    1165           0 :       return 0;
    1166             : 
    1167           6 :     case fd_vote_instruction_enum_initialize_account_v2:
    1168           6 :       return deser_vote_init_v2( &instruction->initialize_account_v2, p, sz );
    1169             : 
    1170           0 :     case fd_vote_instruction_enum_update_commission_collector:
    1171           0 :       READ_ENUM( instruction->update_commission_collector.discriminant, 2U, p, sz );
    1172           0 :       return 0;
    1173             : 
    1174           0 :     case fd_vote_instruction_enum_update_commission_bps:
    1175           0 :       READ_U16( instruction->update_commission_bps.commission_bps, p, sz );
    1176           0 :       READ_ENUM( instruction->update_commission_bps.kind.discriminant, 2U, p, sz );
    1177           0 :       return 0;
    1178             : 
    1179           0 :     case fd_vote_instruction_enum_deposit_delegator_rewards:
    1180           0 :       READ_U64( instruction->deposit_delegator_rewards.deposit, p, sz );
    1181           0 :       return 0;
    1182             : 
    1183           0 :     default:
    1184           0 :       return 1;
    1185          12 :   }
    1186          12 : }
    1187             : 
    1188             : fd_vote_instruction_t *
    1189             : fd_vote_instruction_deserialize( fd_vote_instruction_t * instruction,
    1190             :                                  uchar const *           data,
    1191          12 :                                  ulong                   data_sz ) {
    1192          12 :   CHECK_RET_NULL( !fd_vote_instruction_deserialize_inner( instruction, data, data_sz ) );
    1193          12 :   return instruction;
    1194          12 : }

Generated by: LCOV version 1.14