LCOV - code coverage report
Current view: top level - choreo/tower - fd_tower_serdes.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 214 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 9 0.0 %

          Line data    Source code
       1             : #include "fd_tower_serdes.h"
       2             : #include "fd_tower.h"
       3             : 
       4           0 : #define DE( T, name ) do {                                \
       5           0 :     if( FD_UNLIKELY( buf_sz<sizeof(T) ) ) return -1;      \
       6           0 :     serde->name = FD_LOAD( T, buf );                      \
       7           0 :     buf    += sizeof(T);                                  \
       8           0 :     buf_sz -= sizeof(T);                                  \
       9           0 : } while(0)
      10             : 
      11           0 : #define SER( T, name ) do {                               \
      12           0 :     if( FD_UNLIKELY( off+sizeof(T)>buf_max ) ) return -1; \
      13           0 :     FD_STORE( T, buf+off, serde->name );                  \
      14           0 :     off += sizeof(T);                                     \
      15           0 : } while(0)
      16             : 
      17           0 : #define SER_SHORT_U16( name ) do {                        \
      18           0 :     uchar tmp[3];                                         \
      19           0 :     ulong n = ser_short_u16( tmp, serde->name );          \
      20           0 :     if( FD_UNLIKELY( off+n>buf_max ) ) return -1;         \
      21           0 :     fd_memcpy( buf+off, tmp, n );                         \
      22           0 :     off += n;                                             \
      23           0 : } while(0)
      24             : 
      25           0 : #define SER_VAR_INT( name ) do {                          \
      26           0 :     uchar tmp[10];                                        \
      27           0 :     ulong n = ser_var_int( tmp, serde->name );            \
      28           0 :     if( FD_UNLIKELY( off+n>buf_max ) ) return -1;         \
      29           0 :     fd_memcpy( buf+off, tmp, n );                         \
      30           0 :     off += n;                                             \
      31           0 : } while(0)
      32             : 
      33             : static int
      34           0 : de_short_u16( ushort * dst, uchar const ** src, ulong * src_sz ) {
      35           0 :   uchar const * s = *src;
      36           0 :   if( FD_UNLIKELY( *src_sz<1 ) ) return -1;
      37           0 :   if( FD_LIKELY( !( 0x80U & s[0] ) ) ) {
      38           0 :     *dst = (ushort)s[0];
      39           0 :     *src += 1;
      40           0 :     *src_sz -= 1;
      41           0 :     return 0;
      42           0 :   }
      43           0 :   if( FD_UNLIKELY( *src_sz<2 ) ) return -1;
      44           0 :   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           0 :   if( FD_UNLIKELY( *src_sz<3    ) ) return -1;
      52           0 :   if( FD_UNLIKELY( 0x80U & s[2] ) ) return -1; /* 3rd byte is final; continuation bit is invalid */
      53           0 :   if( FD_UNLIKELY( !s[2]        ) ) return -1; /* non-canonical: value fits in 2 bytes */
      54           0 :   ulong val = (ulong)(s[0]&0x7FUL) + (((ulong)(s[1]&0x7FUL))<<7) + (((ulong)s[2])<<14);
      55           0 :   if( FD_UNLIKELY( val>USHORT_MAX ) ) return -1;
      56           0 :   *dst = (ushort)val;
      57           0 :   *src += 3;
      58           0 :   *src_sz -= 3;
      59           0 :   return 0;
      60           0 : }
      61             : 
      62             : static int
      63           0 : de_var_int( ulong * dst, uchar const ** src, ulong * src_sz ) {
      64           0 :   *dst = 0;
      65           0 :   ulong bit = 0;
      66           0 :   while( FD_LIKELY( bit < 64 ) ) {
      67           0 :     if( FD_UNLIKELY( !*src_sz ) ) return -1;
      68           0 :     uchar byte = **src;
      69           0 :     (*src)++; (*src_sz)--;
      70           0 :     *dst |= (ulong)(byte & 0x7FUL) << bit;
      71           0 :     if( FD_LIKELY( (byte & 0x80U) == 0U ) ) {
      72           0 :       if( FD_UNLIKELY( (*dst>>bit) != byte                ) ) return -1;
      73           0 :       if( FD_UNLIKELY( byte==0U && (bit!=0U || *dst!=0UL) ) ) return -1;
      74           0 :       return 0;
      75           0 :     }
      76           0 :     bit += 7;
      77           0 :   }
      78           0 :   return -1;
      79           0 : }
      80             : 
      81             : static ulong
      82           0 : ser_short_u16( uchar * dst, ushort val ) {
      83           0 :   if     ( FD_LIKELY( val < 0x80U ) ) {
      84           0 :     dst[0] = (uchar)val;
      85           0 :     return 1;
      86           0 :   }
      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           0 : }
      99             : 
     100             : static ulong
     101           0 : ser_var_int( uchar * dst, ulong val ) {
     102           0 :   ulong off = 0;
     103           0 :   while( FD_LIKELY( val >= 0x80UL ) ) {
     104           0 :     dst[off] = (uchar)((val & 0x7FUL) | 0x80U);
     105           0 :     val >>= 7;
     106           0 :     off  += 1;
     107           0 :   }
     108           0 :   dst[off] = (uchar)val;
     109           0 :   return off + 1;
     110           0 : }
     111             : 
     112             : int
     113             : fd_compact_tower_sync_de( fd_compact_tower_sync_serde_t * serde,
     114             :                           uchar const *                   buf,
     115           0 :                           ulong                           buf_sz ) {
     116           0 :   DE( ulong, root );
     117           0 :   if( FD_UNLIKELY( de_short_u16( &serde->lockouts_cnt, &buf, &buf_sz ) ) ) return -1;
     118           0 :   if( FD_UNLIKELY( serde->lockouts_cnt > FD_TOWER_VOTE_MAX ) ) return -1;
     119           0 :   for( ulong i = 0; i < serde->lockouts_cnt; i++ ) {
     120           0 :     if( FD_UNLIKELY( de_var_int( &serde->lockouts[i].offset, &buf, &buf_sz ) ) ) return -1;
     121           0 :     DE( uchar, lockouts[i].confirmation_count );
     122           0 :   }
     123           0 :   DE( fd_hash_t, hash             );
     124           0 :   DE( uchar,     timestamp_option );
     125           0 :   if( FD_UNLIKELY( serde->timestamp_option!=1 && serde->timestamp_option!=0 ) ) return -1;
     126           0 :   if( FD_LIKELY( serde->timestamp_option ) ) {
     127           0 :     DE( long, timestamp );
     128           0 :   }
     129           0 :   DE( fd_hash_t, block_id );
     130           0 :   return 0;
     131           0 : }
     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           0 :                            ulong *                               buf_sz ) {
     138           0 :   if( FD_UNLIKELY( serde->lockouts_cnt > FD_TOWER_VOTE_MAX ) ) return -1;
     139           0 :   ulong off = 0;
     140           0 :   SER( ulong, root );
     141           0 :   SER_SHORT_U16( lockouts_cnt );
     142           0 :   for( ulong i = 0; i < serde->lockouts_cnt; i++ ) {
     143           0 :     SER_VAR_INT( lockouts[i].offset );
     144           0 :     SER( uchar, lockouts[i].confirmation_count );
     145           0 :   }
     146           0 :   SER( fd_hash_t, hash             );
     147           0 :   SER( uchar,     timestamp_option );
     148           0 :   if( FD_LIKELY( serde->timestamp_option ) ) {
     149           0 :     SER( long, timestamp );
     150           0 :   }
     151           0 :   SER( fd_hash_t, block_id );
     152           0 :   if( FD_LIKELY( buf_sz ) ) *buf_sz = off;
     153           0 :   return 0;
     154           0 : }
     155             : 
     156             : fd_vote_acc_desc_t *
     157             : fd_vote_acc_desc( fd_vote_acc_desc_t * desc,
     158             :                   uchar const *        data,
     159           0 :                   ulong                data_sz ) {
     160           0 :   if( FD_UNLIKELY( data_sz < sizeof(uint) ) ) return NULL;
     161           0 :   uint kind = FD_LOAD( uint, data );
     162           0 :   fd_vote_acc_t const * voter = fd_type_pun_const( data );
     163           0 :   void const *  vote_cnt_p;
     164           0 :   ulong         vote_stride;
     165           0 :   switch( kind ) {
     166           0 :   case FD_VOTE_ACC_V2:
     167           0 :     vote_cnt_p  = &voter->v2.votes_cnt;
     168           0 :     vote_stride = sizeof(fd_vote_acc_vote_v2_t);
     169           0 :     break;
     170           0 :   case FD_VOTE_ACC_V3:
     171           0 :     vote_cnt_p  = &voter->v3.votes_cnt;
     172           0 :     vote_stride = sizeof(fd_vote_acc_vote_t);
     173           0 :     break;
     174           0 :   case FD_VOTE_ACC_V4:
     175           0 :     if( FD_UNLIKELY( (ulong)&voter->v4.has_bls_pubkey_compressed >= (ulong)data+data_sz ) ) return NULL;
     176           0 :     vote_cnt_p  = voter->v4.bls_pubkey_compressed + (!!voter->v4.has_bls_pubkey_compressed * sizeof(voter->v4.bls_pubkey_compressed));
     177           0 :     vote_stride = sizeof(fd_vote_acc_vote_t);
     178           0 :     break;
     179           0 :   default:
     180           0 :     return NULL;
     181           0 :   }
     182           0 :   if( FD_UNLIKELY( (ulong)vote_cnt_p+sizeof(ulong) > (ulong)data+data_sz ) ) return NULL;
     183           0 :   ulong vote_cnt = FD_LOAD( ulong, vote_cnt_p );
     184             :   /* FIXME silent truncation is questionable behavior */
     185           0 :   vote_cnt = fd_ulong_min( vote_cnt, FD_TOWER_VOTE_MAX );
     186           0 :   ulong vote_hi_p = (ulong)vote_cnt_p + sizeof(ulong) + vote_cnt*vote_stride;
     187           0 :   if( FD_UNLIKELY( vote_hi_p > (ulong)data+data_sz ) ) return NULL;
     188             :   /* Option<ulong> "root_slot" follows vote array */
     189           0 :   ulong root_slot = ULONG_MAX;
     190           0 :   if( FD_UNLIKELY( vote_hi_p+1UL > (ulong)data+data_sz ) ) return NULL;
     191           0 :   int root_opt = FD_LOAD( uchar, (void const *)vote_hi_p );
     192           0 :   if( root_opt==1 ) {
     193           0 :     if( FD_UNLIKELY( vote_hi_p+9UL > (ulong)data+data_sz ) ) return NULL;
     194           0 :     root_slot = FD_LOAD( ulong, (void const *)(vote_hi_p+1UL) );
     195           0 :   } else if( FD_UNLIKELY( root_opt!=0 ) ) {
     196           0 :     return NULL;
     197           0 :   }
     198           0 :   *desc = (fd_vote_acc_desc_t){
     199           0 :     .kind        = (int)kind,
     200           0 :     .votes_off   = (ushort)( (ulong)vote_cnt_p + sizeof(ulong) - (ulong)data ),
     201           0 :     .vote_cnt    = (uchar)vote_cnt,
     202           0 :     .vote_stride = (uchar)vote_stride,
     203           0 :     .root_slot   = root_slot
     204           0 :   };
     205           0 :   return desc;
     206           0 : }
     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             : int
     231             : fd_compact_tower_sync_to_votes( fd_compact_tower_sync_serde_t const * serde,
     232             :                                 fd_tower_vote_t *                     out,
     233             :                                 ulong *                               out_cnt,
     234           0 :                                 ulong *                               out_root ) {
     235           0 :   if( FD_UNLIKELY( serde->lockouts_cnt>FD_TOWER_VOTE_MAX ) ) return -1;
     236             : 
     237           0 :   fd_tower_vote_t votes[ FD_TOWER_VOTE_MAX ];
     238           0 :   ulong slot      = fd_ulong_if( serde->root==ULONG_MAX, 0UL, serde->root );
     239           0 :   ulong prev_conf = FD_TOWER_VOTE_MAX+1UL;
     240             :   /* Only the vote below is checked, Agave pops expired votes from the
     241             :      top only, so a deeper expired vote is a normal state. */
     242           0 :   for( ulong i=0UL; i<serde->lockouts_cnt; i++ ) {
     243           0 :     ulong offset = serde->lockouts[ i ].offset;
     244           0 :     ulong conf   = serde->lockouts[ i ].confirmation_count;
     245           0 :     int repeats_slot = !offset && (i || serde->root!=ULONG_MAX);
     246           0 :     if( FD_UNLIKELY( repeats_slot || offset>ULONG_MAX-slot || !conf ||
     247           0 :                      conf>=prev_conf || conf>FD_TOWER_VOTE_MAX ) ) return -1;
     248           0 :     if( FD_UNLIKELY( i && offset>(1UL<<prev_conf) ) ) return -1;
     249           0 :     slot += offset;
     250           0 :     votes[ i ] = (fd_tower_vote_t){ .slot=slot, .conf=conf };
     251           0 :     prev_conf = conf;
     252           0 :   }
     253           0 :   fd_memcpy( out, votes, serde->lockouts_cnt*sizeof(fd_tower_vote_t) );
     254           0 :   *out_cnt  = serde->lockouts_cnt;
     255           0 :   *out_root = serde->root;
     256           0 :   return 0;
     257           0 : }
     258             : 
     259             : #undef DE
     260             : #undef SER

Generated by: LCOV version 1.14