LCOV - code coverage report
Current view: top level - choreo/tower - fd_tower_serdes.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 156 193 80.8 %
Date: 2026-09-17 04:28:31 Functions: 7 8 87.5 %

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

Generated by: LCOV version 1.14