LCOV - code coverage report
Current view: top level - choreo/tower - fd_tower_serdes.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 142 192 74.0 %
Date: 2026-06-09 08:01:34 Functions: 7 8 87.5 %

          Line data    Source code
       1             : #include "fd_tower_serdes.h"
       2             : #include "fd_tower.h"
       3             : 
       4        2013 : #define DE( T, name ) do {                                \
       5        2013 :     if( FD_UNLIKELY( buf_sz<sizeof(T) ) ) return -1;      \
       6        2013 :     serde->name = *(T const *)fd_type_pun_const( buf );   \
       7        1536 :     buf    += sizeof(T);                                  \
       8        1536 :     buf_sz -= sizeof(T);                                  \
       9        1536 : } while(0)
      10             : 
      11         435 : #define SER( T, name ) do {                               \
      12         435 :     if( FD_UNLIKELY( off+sizeof(T)>buf_max ) ) return -1; \
      13         435 :     FD_STORE( T, buf+off, serde->name );                  \
      14         435 :     off += sizeof(T);                                     \
      15         435 : } while(0)
      16             : 
      17             : static int
      18         483 : de_short_u16( ushort * dst, uchar const ** src, ulong * src_sz ) {
      19         483 :   uchar const * s = *src;
      20         483 :   if( FD_UNLIKELY( *src_sz<1 ) ) return -1;
      21         474 :   if( FD_LIKELY( !( 0x80U & s[0] ) ) ) {
      22         468 :     *dst = (ushort)s[0];
      23         468 :     *src += 1;
      24         468 :     *src_sz -= 1;
      25         468 :     return 0;
      26         468 :   }
      27           6 :   if( FD_UNLIKELY( *src_sz<2 ) ) return -1;
      28           3 :   if( FD_LIKELY( !( 0x80U & s[1] ) ) ) {
      29           0 :     if( FD_UNLIKELY( !s[1] ) ) return -1; /* non-canonical: value fits in 1 byte */
      30           0 :     *dst = (ushort)( (ulong)(s[0]&0x7FUL) + (((ulong)s[1])<<7) );
      31           0 :     *src += 2;
      32           0 :     *src_sz -= 2;
      33           0 :     return 0;
      34           0 :   }
      35           3 :   if( FD_UNLIKELY( *src_sz<3    ) ) return -1;
      36           3 :   if( FD_UNLIKELY( 0x80U & s[2] ) ) return -1; /* 3rd byte is final; continuation bit is invalid */
      37           3 :   if( FD_UNLIKELY( !s[2]        ) ) return -1; /* non-canonical: value fits in 2 bytes */
      38           3 :   ulong val = (ulong)(s[0]&0x7FUL) + (((ulong)(s[1]&0x7FUL))<<7) + (((ulong)s[2])<<14);
      39           3 :   if( FD_UNLIKELY( val>USHORT_MAX ) ) return -1;
      40           3 :   *dst = (ushort)val;
      41           3 :   *src += 3;
      42           3 :   *src_sz -= 3;
      43           3 :   return 0;
      44           3 : }
      45             : 
      46             : static int
      47         438 : de_var_int( ulong * dst, uchar const ** src, ulong * src_sz ) {
      48         438 :   *dst = 0;
      49         438 :   ulong bit = 0;
      50         501 :   while( FD_LIKELY( bit < 64 ) ) {
      51         498 :     if( FD_UNLIKELY( !*src_sz ) ) return -1;
      52         489 :     uchar byte = **src;
      53         489 :     (*src)++; (*src_sz)--;
      54         489 :     *dst |= (ulong)(byte & 0x7FUL) << bit;
      55         489 :     if( FD_LIKELY( (byte & 0x80U) == 0U ) ) {
      56         426 :       if( FD_UNLIKELY( (*dst>>bit) != byte                ) ) return -1;
      57         426 :       if( FD_UNLIKELY( byte==0U && (bit!=0U || *dst!=0UL) ) ) return -1;
      58         423 :       return 0;
      59         426 :     }
      60          63 :     bit += 7;
      61          63 :   }
      62           3 :   return -1;
      63         438 : }
      64             : 
      65             : static ulong
      66          51 : ser_short_u16( uchar * dst, ushort val ) {
      67          51 :   if     ( FD_LIKELY( val < 0x80U ) ) {
      68          51 :     dst[0] = (uchar)val;
      69          51 :     return 1;
      70          51 :   }
      71           0 :   else if( FD_LIKELY( val < 0x4000U ) ) {
      72           0 :     dst[0] = (uchar)((val & 0x7FUL) | 0x80U);
      73           0 :     dst[1] = (uchar)(val >> 7);
      74           0 :     return 2;
      75           0 :   }
      76           0 :   else {
      77           0 :     dst[0] = (uchar)((val & 0x7FUL) | 0x80U);
      78           0 :     dst[1] = (uchar)(((val >> 7) & 0x7FUL) | 0x80U);
      79           0 :     dst[2] = (uchar)(val >> 14);
      80           0 :     return 3;
      81           0 :   }
      82          51 : }
      83             : 
      84             : static ulong
      85         216 : ser_var_int( uchar * dst, ulong val ) {
      86         216 :   ulong off = 0;
      87         243 :   while( FD_LIKELY( val >= 0x80UL ) ) {
      88          27 :     dst[off] = (uchar)((val & 0x7FUL) | 0x80U);
      89          27 :     val >>= 7;
      90          27 :     off  += 1;
      91          27 :   }
      92         216 :   dst[off] = (uchar)val;
      93         216 :   return off + 1;
      94         216 : }
      95             : 
      96             : int
      97             : fd_compact_tower_sync_de( fd_compact_tower_sync_serde_t * serde,
      98             :                           uchar const *                   buf,
      99         537 :                           ulong                           buf_sz ) {
     100         537 :   DE( ulong, root );
     101         483 :   if( FD_UNLIKELY( de_short_u16( &serde->lockouts_cnt, &buf, &buf_sz ) ) ) return -1;
     102         471 :   if( FD_UNLIKELY( serde->lockouts_cnt > FD_TOWER_VOTE_MAX ) ) return -1;
     103         885 :   for( ulong i = 0; i < serde->lockouts_cnt; i++ ) {
     104         438 :     if( FD_UNLIKELY( de_var_int( &serde->lockouts[i].offset, &buf, &buf_sz ) ) ) return -1;
     105         423 :     DE( uchar, lockouts[i].confirmation_count );
     106         423 :   }
     107         447 :   DE( fd_hash_t, hash             );
     108         255 :   DE( uchar,     timestamp_option );
     109         249 :   if( FD_UNLIKELY( serde->timestamp_option!=1 && serde->timestamp_option!=0 ) ) return -1;
     110         243 :   if( FD_LIKELY( serde->timestamp_option ) ) {
     111         135 :     DE( long, timestamp );
     112         135 :   }
     113         216 :   DE( fd_hash_t, block_id );
     114          21 :   return 0;
     115         216 : }
     116             : 
     117             : int
     118             : fd_compact_tower_sync_ser( fd_compact_tower_sync_serde_t const * serde,
     119             :                            uchar *                               buf,
     120             :                            ulong                                 buf_max,
     121          51 :                            ulong *                               buf_sz ) {
     122          51 :   ulong off = 0;
     123          51 :   SER( ulong, root );
     124          51 :   off += ser_short_u16( buf+off, serde->lockouts_cnt );
     125          51 :   if( FD_UNLIKELY( serde->lockouts_cnt > FD_TOWER_VOTE_MAX ) ) return -1;
     126         267 :   for( ulong i = 0; i < serde->lockouts_cnt; i++ ) {
     127         216 :     off += ser_var_int( buf+off, serde->lockouts[i].offset );
     128         216 :     SER( uchar, lockouts[i].confirmation_count );
     129         216 :   }
     130          51 :   SER( fd_hash_t, hash             );
     131          51 :   SER( uchar,     timestamp_option );
     132          51 :   if( FD_LIKELY( serde->timestamp_option ) ) {
     133          15 :     SER( long, timestamp );
     134          15 :   }
     135          51 :   SER( fd_hash_t, block_id );
     136          51 :   if( FD_LIKELY( buf_sz ) ) *buf_sz = off;
     137          51 :   return 0;
     138          51 : }
     139             : 
     140             : fd_vote_acc_desc_t *
     141             : fd_vote_acc_desc( fd_vote_acc_desc_t * desc,
     142             :                   uchar const *        data,
     143         156 :                   ulong                data_sz ) {
     144         156 :   if( FD_UNLIKELY( data_sz < sizeof(uint) ) ) return NULL;
     145         156 :   uint kind = FD_LOAD( uint, data );
     146         156 :   fd_vote_acc_t const * voter = fd_type_pun_const( data );
     147         156 :   void const *  vote_cnt_p;
     148         156 :   ulong         vote_stride;
     149         156 :   switch( kind ) {
     150           6 :   case FD_VOTE_ACC_V2:
     151           6 :     vote_cnt_p  = &voter->v2.votes_cnt;
     152           6 :     vote_stride = sizeof(fd_vote_acc_vote_v2_t);
     153           6 :     break;
     154         147 :   case FD_VOTE_ACC_V3:
     155         147 :     vote_cnt_p  = &voter->v3.votes_cnt;
     156         147 :     vote_stride = sizeof(fd_vote_acc_vote_t);
     157         147 :     break;
     158           3 :   case FD_VOTE_ACC_V4:
     159           3 :     if( FD_UNLIKELY( (ulong)&voter->v4.has_bls_pubkey_compressed >= (ulong)data+data_sz ) ) return NULL;
     160           3 :     vote_cnt_p  = voter->v4.bls_pubkey_compressed + (!!voter->v4.has_bls_pubkey_compressed * sizeof(voter->v4.bls_pubkey_compressed));
     161           3 :     vote_stride = sizeof(fd_vote_acc_vote_t);
     162           3 :     break;
     163           0 :   default:
     164           0 :     return NULL;
     165         156 :   }
     166         156 :   if( FD_UNLIKELY( (ulong)vote_cnt_p+sizeof(ulong) > (ulong)data+data_sz ) ) return NULL;
     167         156 :   ulong vote_cnt = FD_LOAD( ulong, vote_cnt_p );
     168             :   /* FIXME silent truncation is questionable behavior */
     169         156 :   vote_cnt = fd_ulong_min( vote_cnt, FD_TOWER_VOTE_MAX );
     170         156 :   ulong vote_hi_p = (ulong)vote_cnt_p + sizeof(ulong) + vote_cnt*vote_stride;
     171         156 :   if( FD_UNLIKELY( vote_hi_p > (ulong)data+data_sz ) ) return NULL;
     172             :   /* Option<ulong> "root_slot" follows vote array */
     173         156 :   ulong root_slot = ULONG_MAX;
     174         156 :   if( FD_UNLIKELY( vote_hi_p+1UL > (ulong)data+data_sz ) ) return NULL;
     175         156 :   int root_opt = FD_LOAD( uchar, (void const *)vote_hi_p );
     176         156 :   if( root_opt==1 ) {
     177          15 :     if( FD_UNLIKELY( vote_hi_p+9UL > (ulong)data+data_sz ) ) return NULL;
     178          15 :     root_slot = FD_LOAD( ulong, (void const *)(vote_hi_p+1UL) );
     179         141 :   } else if( FD_UNLIKELY( root_opt!=0 ) ) {
     180           0 :     return NULL;
     181           0 :   }
     182         156 :   *desc = (fd_vote_acc_desc_t){
     183         156 :     .kind        = (int)kind,
     184         156 :     .votes_off   = (ushort)( (ulong)vote_cnt_p + sizeof(ulong) - (ulong)data ),
     185         156 :     .vote_cnt    = (uchar)vote_cnt,
     186         156 :     .vote_stride = (uchar)vote_stride,
     187         156 :     .root_slot   = root_slot
     188         156 :   };
     189         156 :   return desc;
     190         156 : }
     191             : 
     192             : int
     193             : fd_txn_parse_simple_vote( fd_txn_t const * txn,
     194             :                           uchar    const * payload,
     195             :                           fd_pubkey_t *    opt_identity,
     196             :                           fd_pubkey_t *    opt_vote_acct,
     197           0 :                           ulong *          opt_vote_slot ) {
     198           0 :   fd_txn_instr_t const * instr      = &txn->instr[ 0 ];
     199           0 :   ulong required_accts = txn->signature_cnt==1 ? 2UL : 3UL;
     200           0 :   if( FD_UNLIKELY( !fd_txn_is_simple_vote_transaction( txn, payload ) || instr->data_sz < sizeof(uint) || txn->acct_addr_cnt < required_accts ) ) return 0;
     201             : 
     202           0 :   uchar const *          instr_data = payload + instr->data_off;
     203           0 :   uint                   kind       = fd_uint_load_4_fast( instr_data );
     204             :   /* Older vote instruction kinds are deprecated / ignored */
     205           0 :   if( FD_UNLIKELY( kind == FD_VOTE_IX_KIND_TOWER_SYNC || kind == FD_VOTE_IX_KIND_TOWER_SYNC_SWITCH ) ) {
     206           0 :     fd_compact_tower_sync_serde_t compact_tower_sync_serde[ 1 ];
     207           0 :     int err = fd_compact_tower_sync_de( compact_tower_sync_serde, instr_data + sizeof(uint), instr->data_sz - sizeof(uint) );
     208           0 :     if( FD_LIKELY( !err ) ) {
     209           0 :       if( !!opt_vote_slot ) {
     210           0 :         *opt_vote_slot =  fd_ulong_if( compact_tower_sync_serde->root==ULONG_MAX, 0, compact_tower_sync_serde->root );
     211           0 :         for( ulong i = 0; i < compact_tower_sync_serde->lockouts_cnt; i++ ) {
     212           0 :           if( FD_UNLIKELY( __builtin_uaddl_overflow( *opt_vote_slot, compact_tower_sync_serde->lockouts[ i ].offset, opt_vote_slot ) ) ) return 0;
     213           0 :         }
     214           0 :       }
     215           0 :       fd_pubkey_t const * accs = (fd_pubkey_t const *)fd_type_pun_const( payload + txn->acct_addr_off );
     216           0 :       if( !!opt_vote_acct ) {
     217           0 :         if( FD_UNLIKELY( txn->signature_cnt==1 ) ) *opt_vote_acct = *(fd_pubkey_t const *)fd_type_pun_const( &accs[ 1 ] ); /* identity and authority same, account idx 1 is the vote account address */
     218           0 :         else                                       *opt_vote_acct = *(fd_pubkey_t const *)fd_type_pun_const( &accs[ 2 ] ); /* identity and authority diff, account idx 2 is the vote account address */
     219           0 :       }
     220           0 :       if( !!opt_identity ) {
     221           0 :         *opt_identity = *(fd_pubkey_t const *)fd_type_pun_const( &accs[ 0 ] );
     222           0 :       }
     223           0 :       return 1;
     224           0 :     }
     225           0 :   }
     226           0 :   return 0;
     227           0 : }
     228             : 
     229             : #undef DE
     230             : #undef SER

Generated by: LCOV version 1.14