LCOV - code coverage report
Current view: top level - choreo/tower - fd_tower_serdes.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 129 176 73.3 %
Date: 2026-04-01 06:30:45 Functions: 10 11 90.9 %

          Line data    Source code
       1             : #include "fd_tower_serdes.h"
       2             : #include "fd_tower.h"
       3             : 
       4             : #define SHORTVEC 0
       5             : 
       6        2259 : #define DE( T, name ) do {                                \
       7        2259 :     if( FD_UNLIKELY( buf_sz<sizeof(T) ) ) return -1;      \
       8        2259 :     serde->name = *(T const *)fd_type_pun_const( buf );   \
       9        1782 :     buf    += sizeof(T);                                  \
      10        1782 :     buf_sz -= sizeof(T);                                  \
      11        1782 : } while(0)
      12             : 
      13         681 : #define SER( T, name ) do {                               \
      14         681 :     if( FD_UNLIKELY( off+sizeof(T)>buf_max ) ) return -1; \
      15         681 :     FD_STORE( T, buf+off, serde->name );                  \
      16         681 :     off += sizeof(T);                                     \
      17         681 : } while(0)
      18             : 
      19             : static int
      20         507 : de_short_u16( ushort * dst, uchar const ** src, ulong * src_sz ) {
      21         507 :   uchar const * s = *src;
      22         507 :   if( FD_UNLIKELY( *src_sz<1 ) ) return -1;
      23         498 :   if( FD_LIKELY( !( 0x80U & s[0] ) ) ) {
      24         492 :     *dst = (ushort)s[0];
      25         492 :     *src += 1;
      26         492 :     *src_sz -= 1;
      27         492 :     return 0;
      28         492 :   }
      29           6 :   if( FD_UNLIKELY( *src_sz<2 ) ) return -1;
      30           3 :   if( FD_LIKELY( !( 0x80U & s[1] ) ) ) {
      31           0 :     if( FD_UNLIKELY( !s[1] ) ) return -1; /* non-canonical: value fits in 1 byte */
      32           0 :     *dst = (ushort)( (ulong)(s[0]&0x7FUL) + (((ulong)s[1])<<7) );
      33           0 :     *src += 2;
      34           0 :     *src_sz -= 2;
      35           0 :     return 0;
      36           0 :   }
      37           3 :   if( FD_UNLIKELY( *src_sz<3    ) ) return -1;
      38           3 :   if( FD_UNLIKELY( 0x80U & s[2] ) ) return -1; /* 3rd byte is final; continuation bit is invalid */
      39           3 :   if( FD_UNLIKELY( !s[2]        ) ) return -1; /* non-canonical: value fits in 2 bytes */
      40           3 :   ulong val = (ulong)(s[0]&0x7FUL) + (((ulong)(s[1]&0x7FUL))<<7) + (((ulong)s[2])<<14);
      41           3 :   if( FD_UNLIKELY( val>USHORT_MAX ) ) return -1;
      42           3 :   *dst = (ushort)val;
      43           3 :   *src += 3;
      44           3 :   *src_sz -= 3;
      45           3 :   return 0;
      46           3 : }
      47             : 
      48             : static int
      49         564 : de_var_int( ulong * dst, uchar const ** src, ulong * src_sz ) {
      50         564 :   *dst = 0;
      51         564 :   ulong bit = 0;
      52         627 :   while( FD_LIKELY( bit < 64 ) ) {
      53         624 :     if( FD_UNLIKELY( !*src_sz ) ) return -1;
      54         615 :     uchar byte = **src;
      55         615 :     (*src)++; (*src_sz)--;
      56         615 :     *dst |= (ulong)(byte & 0x7FUL) << bit;
      57         615 :     if( FD_LIKELY( (byte & 0x80U) == 0U ) ) {
      58         552 :       if( FD_UNLIKELY( (*dst>>bit) != byte                ) ) return -1;
      59         552 :       if( FD_UNLIKELY( byte==0U && (bit!=0U || *dst!=0UL) ) ) return -1;
      60         549 :       return 0;
      61         552 :     }
      62          63 :     bit += 7;
      63          63 :   }
      64           3 :   return -1;
      65         564 : }
      66             : 
      67             : static ulong
      68          75 : ser_short_u16( uchar * dst, ushort val ) {
      69          75 :   if     ( FD_LIKELY( val < 0x80U ) ) {
      70          75 :     dst[0] = (uchar)val;
      71          75 :     return 1;
      72          75 :   }
      73           0 :   else if( FD_LIKELY( val < 0x4000U ) ) {
      74           0 :     dst[0] = (uchar)((val & 0x7FUL) | 0x80U);
      75           0 :     dst[1] = (uchar)(val >> 7);
      76           0 :     return 2;
      77           0 :   }
      78           0 :   else {
      79           0 :     dst[0] = (uchar)((val & 0x7FUL) | 0x80U);
      80           0 :     dst[1] = (uchar)(((val >> 7) & 0x7FUL) | 0x80U);
      81           0 :     dst[2] = (uchar)(val >> 14);
      82           0 :     return 3;
      83           0 :   }
      84          75 : }
      85             : 
      86             : static ulong
      87         342 : ser_var_int( uchar * dst, ulong val ) {
      88         342 :   ulong off = 0;
      89         369 :   while( FD_LIKELY( val >= 0x80UL ) ) {
      90          27 :     dst[off] = (uchar)((val & 0x7FUL) | 0x80U);
      91          27 :     val >>= 7;
      92          27 :     off  += 1;
      93          27 :   }
      94         342 :   dst[off] = (uchar)val;
      95         342 :   return off + 1;
      96         342 : }
      97             : 
      98             : int
      99             : fd_compact_tower_sync_de( fd_compact_tower_sync_serde_t * serde,
     100             :                           uchar const *                   buf,
     101         561 :                           ulong                           buf_sz ) {
     102         561 :   DE( ulong, root );
     103         507 :   if( FD_UNLIKELY( de_short_u16( &serde->lockouts_cnt, &buf, &buf_sz ) ) ) return -1;
     104         495 :   if( FD_UNLIKELY( serde->lockouts_cnt > FD_TOWER_VOTE_MAX ) ) return -1;
     105        1035 :   for( ulong i = 0; i < serde->lockouts_cnt; i++ ) {
     106         564 :     if( FD_UNLIKELY( de_var_int( &serde->lockouts[i].offset, &buf, &buf_sz ) ) ) return -1;
     107         549 :     DE( uchar, lockouts[i].confirmation_count );
     108         549 :   }
     109         471 :   DE( fd_hash_t, hash             );
     110         279 :   DE( uchar,     timestamp_option );
     111         273 :   if( FD_UNLIKELY( serde->timestamp_option!=1 && serde->timestamp_option!=0 ) ) return -1;
     112         267 :   if( FD_LIKELY( serde->timestamp_option ) ) {
     113         159 :     DE( long, timestamp );
     114         159 :   }
     115         240 :   DE( fd_hash_t, block_id );
     116          45 :   return 0;
     117         240 : }
     118             : 
     119             : int
     120             : fd_compact_tower_sync_ser( fd_compact_tower_sync_serde_t const * serde,
     121             :                            uchar *                               buf,
     122             :                            ulong                                 buf_max,
     123          75 :                            ulong *                               buf_sz ) {
     124          75 :   ulong off = 0;
     125          75 :   SER( ulong, root );
     126          75 :   off += ser_short_u16( buf+off, serde->lockouts_cnt );
     127          75 :   if( FD_UNLIKELY( serde->lockouts_cnt > FD_TOWER_VOTE_MAX ) ) return -1;
     128         417 :   for( ulong i = 0; i < serde->lockouts_cnt; i++ ) {
     129         342 :     off += ser_var_int( buf+off, serde->lockouts[i].offset );
     130         342 :     SER( uchar, lockouts[i].confirmation_count );
     131         342 :   }
     132          75 :   SER( fd_hash_t, hash             );
     133          75 :   SER( uchar,     timestamp_option );
     134          75 :   if( FD_LIKELY( serde->timestamp_option ) ) {
     135          39 :     SER( long, timestamp );
     136          39 :   }
     137          75 :   SER( fd_hash_t, block_id );
     138          75 :   if( FD_LIKELY( buf_sz ) ) *buf_sz = off;
     139          75 :   return 0;
     140          75 : }
     141             : 
     142             : static fd_vote_acc_vote_t const *
     143           9 : v4_off( fd_vote_acc_t const * voter ) {
     144           9 :   return (fd_vote_acc_vote_t const *)( voter->v4.bls_pubkey_compressed + voter->v4.has_bls_pubkey_compressed * sizeof(voter->v4.bls_pubkey_compressed) + sizeof(ulong) );
     145           9 : }
     146             : 
     147             : ulong
     148         726 : fd_vote_acc_vote_cnt( uchar const * vote_account_data ) {
     149         726 :   fd_vote_acc_t const * voter = (fd_vote_acc_t const *)fd_type_pun_const( vote_account_data );
     150         726 :   switch( voter->kind ) {
     151           6 :   case FD_VOTE_ACC_V4: return fd_ulong_load_8( voter->v4.bls_pubkey_compressed + voter->v4.has_bls_pubkey_compressed * sizeof(voter->v4.bls_pubkey_compressed) );
     152         612 :   case FD_VOTE_ACC_V3: return voter->v3.votes_cnt;
     153         108 :   case FD_VOTE_ACC_V2: return voter->v2.votes_cnt;
     154           0 :   default: FD_LOG_HEXDUMP_CRIT(( "bad voter", vote_account_data, 3762 ));
     155         726 :   }
     156         726 : }
     157             : 
     158             : /* fd_vote_acc_vote_slot takes a voter's vote account data and returns the
     159             :    voter's most recent vote slot in the tower.  Returns ULONG_MAX if
     160             :    they have an empty tower. */
     161             : 
     162             : ulong
     163          15 : fd_vote_acc_vote_slot( uchar const * vote_account_data ) {
     164          15 :   fd_vote_acc_t const * voter = (fd_vote_acc_t const *)fd_type_pun_const( vote_account_data );
     165          15 :   ulong              cnt   = fd_vote_acc_vote_cnt( vote_account_data );
     166          15 :   switch( voter->kind ) {
     167           3 :   case FD_VOTE_ACC_V4: return cnt ? v4_off( voter )[cnt-1].slot : ULONG_MAX;
     168           9 :   case FD_VOTE_ACC_V3: return cnt ? voter->v3.votes[cnt-1].slot : ULONG_MAX;
     169           3 :   case FD_VOTE_ACC_V2: return cnt ? voter->v2.votes[cnt-1].slot : ULONG_MAX;
     170           0 :   default: FD_LOG_HEXDUMP_CRIT(( "bad voter", vote_account_data, 3762 ));
     171          15 :   }
     172          15 : }
     173             : 
     174             : /* fd_vote_acc_root_slot takes a voter's vote account data and returns the
     175             :    voter's root slot.  Returns ULONG_MAX if they don't have a root. */
     176             : 
     177             : ulong
     178         147 : fd_vote_acc_root_slot( uchar const * vote_account_data ) {
     179         147 :   fd_vote_acc_t const * voter = (fd_vote_acc_t const *)fd_type_pun_const( vote_account_data );
     180         147 :   ulong              cnt   = fd_vote_acc_vote_cnt( vote_account_data );
     181         147 :   switch( voter->kind ) {
     182           3 :   case FD_VOTE_ACC_V4: { uchar root_option = fd_uchar_load_1_fast( (uchar *)&v4_off( voter )[cnt] ); return root_option ? fd_ulong_load_8_fast( (uchar *)&v4_off( voter )[cnt] + 1UL ) : ULONG_MAX; }
     183         138 :   case FD_VOTE_ACC_V3: { uchar root_option = fd_uchar_load_1_fast( (uchar *)&voter->v3.votes[cnt] ); return root_option ? fd_ulong_load_8_fast( (uchar *)&voter->v3.votes[cnt] + 1UL ) : ULONG_MAX; }
     184           6 :   case FD_VOTE_ACC_V2: { uchar root_option = fd_uchar_load_1_fast( (uchar *)&voter->v2.votes[cnt] ); return root_option ? fd_ulong_load_8_fast( (uchar *)&voter->v2.votes[cnt] + 1UL ) : ULONG_MAX; }
     185           0 :   default:          FD_LOG_CRIT(( "unhandled kind %u", voter->kind ));
     186         147 :   }
     187         147 : }
     188             : 
     189             : int
     190             : fd_txn_parse_simple_vote( fd_txn_t const * txn,
     191             :                           uchar    const * payload,
     192             :                           fd_pubkey_t *    opt_identity,
     193             :                           fd_pubkey_t *    opt_vote_acct,
     194           0 :                           ulong *          opt_vote_slot ) {
     195           0 :   fd_txn_instr_t const * instr      = &txn->instr[ 0 ];
     196           0 :   ulong required_accts = txn->signature_cnt==1 ? 2UL : 3UL;
     197           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;
     198             : 
     199           0 :   uchar const *          instr_data = payload + instr->data_off;
     200           0 :   uint                   kind       = fd_uint_load_4_fast( instr_data );
     201             :   /* Older vote instruction kinds are deprecated / ignored */
     202           0 :   if( FD_UNLIKELY( kind == FD_VOTE_IX_KIND_TOWER_SYNC || kind == FD_VOTE_IX_KIND_TOWER_SYNC_SWITCH ) ) {
     203           0 :     fd_compact_tower_sync_serde_t compact_tower_sync_serde[ 1 ];
     204           0 :     int err = fd_compact_tower_sync_de( compact_tower_sync_serde, instr_data + sizeof(uint), instr->data_sz - sizeof(uint) );
     205           0 :     if( FD_LIKELY( !err ) ) {
     206           0 :       if( !!opt_vote_slot ) {
     207           0 :         *opt_vote_slot = compact_tower_sync_serde->root;
     208           0 :         for( ulong i = 0; i < compact_tower_sync_serde->lockouts_cnt; i++ ) *opt_vote_slot += compact_tower_sync_serde->lockouts[ i ].offset;
     209           0 :       }
     210           0 :       fd_pubkey_t const * accs = (fd_pubkey_t const *)fd_type_pun_const( payload + txn->acct_addr_off );
     211           0 :       if( !!opt_vote_acct ) {
     212           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 */
     213           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 */
     214           0 :       }
     215           0 :       if( !!opt_identity ) {
     216           0 :         *opt_identity = *(fd_pubkey_t const *)fd_type_pun_const( &accs[ 0 ] );
     217           0 :       }
     218           0 :       return 1;
     219           0 :     }
     220           0 :   }
     221           0 :   return 0;
     222           0 : }

Generated by: LCOV version 1.14