LCOV - code coverage report
Current view: top level - flamenco/gossip - fd_gossip_msg_ser.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 104 155 67.1 %
Date: 2025-11-29 04:46:19 Functions: 5 6 83.3 %

          Line data    Source code
       1             : #include "fd_gossip_types.h"
       2             : #include "fd_gossip_private.h"
       3             : #include "../../util/bits/fd_bits.h"
       4             : 
       5             : #define SER_INIT( payload, payload_sz, offset ) \
       6           9 :   uchar *       _payload    = (payload);        \
       7           9 :   ulong const   _offset     = (offset);         \
       8           9 :   ulong         _i          = (offset);         \
       9           9 :   ulong const   _payload_sz = (payload_sz);     \
      10           9 :   (void)        _offset;                        \
      11           9 :   (void)        _payload_sz;
      12             : 
      13          81 : #define INC( n ) (_i += (ulong)(n))
      14             : 
      15           9 : #define CUR_OFFSET      ((ushort)_i)
      16           9 : #define CURSOR          (_payload+_i)
      17           9 : #define BYTES_CONSUMED  (_i-_offset)
      18             : #define BYTES_REMAINING (_payload_sz-_i)
      19             : 
      20             : static inline ushort
      21          18 : varint_encode( ulong u64, uchar * out_buf ) {
      22          18 :   ushort i = 0UL;
      23          36 :   do {
      24          36 :     uchar byte = (uchar)(u64 & 0x7FUL);
      25          36 :     u64 >>= 7UL;
      26          36 :     if( u64 ) byte |= 0x80U;
      27          36 :     FD_STORE( uchar, out_buf+i, byte );
      28          36 :     i++;
      29          36 :   } while( u64 );
      30          18 :   return i;
      31          18 : }
      32             : 
      33             : static inline ulong
      34             : encode_version( fd_contact_info_t const * contact_info,
      35             :                 uchar *                   out_buf,
      36             :                 ulong                     out_buf_sz,
      37           3 :                 ushort                    start_offset ) {
      38           3 :   SER_INIT( out_buf, out_buf_sz, start_offset );
      39             : 
      40           3 :   INC( varint_encode( contact_info->version.major, CURSOR ) );
      41           3 :   INC( varint_encode( contact_info->version.minor, CURSOR ) );
      42           3 :   INC( varint_encode( contact_info->version.patch, CURSOR ) );
      43             : 
      44           3 :   FD_STORE( uint, CURSOR, contact_info->version.commit )           ; INC( 4U );
      45             : 
      46           3 :   FD_STORE( uint, CURSOR, contact_info->version.feature_set )      ; INC( 4U );
      47             : 
      48           3 :   INC( varint_encode( contact_info->version.client, CURSOR ) );
      49             : 
      50           3 :   return BYTES_CONSUMED;
      51           3 : }
      52             : 
      53             : /* The Gossip encoding of a contact info splits the sockets into
      54             :    two vectors: socket entries (socket_entry_t) and addrs (uint).
      55             :    The sockets are ordered by port values, and the port values
      56             :    are encoded as "offsets" to the previous socket entry's value.
      57             :    addrs is a list of unique IP addresses, and a socket entry's
      58             :    addr_index indexes into this list. To illustrate the conversion:
      59             : 
      60             :    sockets = [
      61             :       { IP: 192.1.1.1, Port: 1000 },  # tag gossip
      62             :       {     192.1.2.1,       2000 },  # tag serve_repair_quic
      63             :       {     0,               0 },     # NULL socket entry for tag RPC
      64             :       {     192.1.1.1,       500 }    # tag rpc pubsub
      65             :   ]
      66             : 
      67             :   would be transformed to:
      68             : 
      69             :   addrs = [
      70             :     192.1.1.1,
      71             :     192.1.2.1
      72             :   ]
      73             : 
      74             :   socket_entries = [
      75             :     { port_offset: 500,  tag: 3, addr_index: 1 }, # first entry's port_offset is the actual port value
      76             :     {              500,       0,             0 }, # second entry is relative to the first entry's port value
      77             :     {              1000,      1,             0 }  # third entry is relative to the second entry's port value
      78             :                                                   # null socket entry is not included
      79             :   ]
      80             : */
      81             : struct socket_entry {
      82             :   ushort port_offset;
      83             :   uchar  tag;
      84             :   uchar  addr_index;
      85             : };
      86             : 
      87             : typedef struct socket_entry socket_entry_t;
      88             : 
      89             : struct socket_ctx {
      90             :   fd_ip4_port_t socket;
      91             :   uchar         socket_tag;
      92             : };
      93             : 
      94             : typedef struct socket_ctx socket_ctx_t;
      95             : 
      96             : #define SORT_NAME           sort_socket_port
      97           0 : #define SORT_KEY_T          socket_ctx_t
      98           0 : #define SORT_BEFORE( a, b ) ( (a).socket.port<(b).socket.port )
      99             : 
     100             : #include "../../util/tmpl/fd_sort.c"
     101             : 
     102          45 : #define FD_CONTACT_INFO_SOCKET_MAX FD_CONTACT_INFO_SOCKET_CNT
     103             : 
     104             : 
     105             : static inline int
     106             : contact_info_convert_sockets( fd_contact_info_t const *             contact_info,
     107             :                               socket_entry_t                        out_sockets_entries[ static FD_CONTACT_INFO_SOCKET_MAX ],
     108             :                               uchar *                               out_socket_entries_cnt,
     109             :                               uint                                  out_addrs[ static FD_CONTACT_INFO_SOCKET_MAX ],
     110           3 :                               uchar *                               out_addrs_cnt ) {
     111           3 :   if( FD_UNLIKELY( !contact_info || !out_socket_entries_cnt || !out_addrs_cnt ) ) {
     112           0 :     FD_LOG_WARNING(( "Invalid arguments to fd_contact_info_convert_sockets" ));
     113           0 :     return -1;
     114           0 :   }
     115             : 
     116           3 :   socket_ctx_t filled_up[ FD_CONTACT_INFO_SOCKET_MAX ];
     117           3 :   ulong filled_up_cnt = 0UL;
     118          45 :   for( ulong j=0; j<FD_CONTACT_INFO_SOCKET_MAX; j++ ) {
     119          42 :     if( contact_info->sockets[j].l!=0 ){
     120           3 :       filled_up[filled_up_cnt].socket = contact_info->sockets[j];
     121             :       /* Convert port to host order. Needed for sorting and because port info
     122             :          is encoded in host order in ContactInfo */
     123           3 :       filled_up[filled_up_cnt].socket.port = fd_ushort_bswap( filled_up[filled_up_cnt].socket.port );
     124           3 :       filled_up[filled_up_cnt].socket_tag = (uchar)j;
     125           3 :       filled_up_cnt++;
     126           3 :     }
     127          42 :   }
     128             : 
     129           3 :   socket_ctx_t scratch[ FD_CONTACT_INFO_SOCKET_MAX ];
     130           3 :   socket_ctx_t * sorted = sort_socket_port_stable_fast( filled_up, filled_up_cnt, scratch );
     131             : 
     132           3 :   uchar addrs_cnt = 0UL;
     133           3 :   uchar socket_entries_cnt = 0UL;
     134             : 
     135             :   /* fill in first entry */
     136           3 :   out_addrs[addrs_cnt++]                              = sorted[0].socket.addr;
     137           3 :   out_sockets_entries[socket_entries_cnt].port_offset = sorted[0].socket.port;
     138           3 :   out_sockets_entries[socket_entries_cnt].addr_index  = 0U;
     139           3 :   out_sockets_entries[socket_entries_cnt++].tag       = sorted[0].socket_tag;
     140             : 
     141           3 :   for( ulong j=1; j<filled_up_cnt; j++ ) {
     142           0 :     socket_ctx_t const * socket = &sorted[j];
     143             : 
     144           0 :     uchar addr_found = 0U;
     145           0 :     for( ulong k=0UL; k<addrs_cnt; k++ ) {
     146           0 :       if( out_addrs[k]==socket->socket.addr ) {
     147             :         /* Already have this address, set index */
     148           0 :         out_sockets_entries[socket_entries_cnt].addr_index = (uchar)k;
     149           0 :         addr_found                                         = 1U;
     150           0 :         break;
     151           0 :       }
     152           0 :     }
     153           0 :     if( !addr_found ) {
     154             :       /* New address, add it */
     155           0 :       out_addrs[addrs_cnt++]                              = socket->socket.addr;
     156           0 :       out_sockets_entries[socket_entries_cnt].addr_index  = (uchar)(addrs_cnt-1);
     157           0 :     }
     158             : 
     159           0 :     out_sockets_entries[socket_entries_cnt].port_offset   = (ushort)(socket->socket.port-sorted[j-1].socket.port);
     160           0 :     out_sockets_entries[socket_entries_cnt++].tag         = socket->socket_tag;
     161           0 :   }
     162             : 
     163           3 :   *out_addrs_cnt              = addrs_cnt;
     164           3 :   *out_socket_entries_cnt     = socket_entries_cnt;
     165           3 :   return 0;
     166           3 : }
     167             : 
     168             : 
     169             : 
     170             : 
     171             : int
     172             : fd_gossip_pull_request_init( uchar *       payload,
     173             :                              ulong         payload_sz,
     174             :                              ulong         num_keys,
     175             :                              ulong         num_bits,
     176             :                              ulong         mask,
     177             :                              uint          mask_bits,
     178             :                              uchar const * contact_info_crds,
     179             :                              ulong         contact_info_crds_sz,
     180             :                              ulong **      out_bloom_keys,
     181             :                              ulong **      out_bloom_bits,
     182             :                              ulong **      out_bits_set,
     183           0 :                              ulong *       out_payload_sz ) {
     184           0 :   FD_TEST( payload_sz<=FD_GOSSIP_MTU );
     185           0 :   SER_INIT( payload, payload_sz, 0U );
     186           0 :   FD_STORE( uint, CURSOR, FD_GOSSIP_MESSAGE_PULL_REQUEST ); INC(          4U );
     187           0 :   FD_STORE( ulong, CURSOR, num_keys                      ); INC(          8U );
     188           0 :   *out_bloom_keys = (ulong *)( CURSOR )                   ; INC( num_keys*8U );
     189             : 
     190           0 :   if( FD_LIKELY( !!num_bits ) ) {
     191             :     /* Bloom bits is a bitvec<u64>, so we need to be careful about converting bloom bits count to vector lengths */
     192           0 :     ulong bloom_vec_len = (num_bits+63UL)/64UL;
     193           0 :     FD_STORE( uchar, CURSOR, 1 )            ; INC(               1U ); /* has_bits */
     194           0 :     FD_STORE( ulong, CURSOR, bloom_vec_len ); INC(               8U );
     195           0 :     *out_bloom_bits = (ulong *)( CURSOR )   ; INC( bloom_vec_len*8U );
     196           0 :   } else {
     197           0 :     FD_STORE( uchar, CURSOR, 0 ); INC( 1U ); /* has_bits */
     198           0 :     *out_bloom_bits = NULL;
     199           0 :   }
     200           0 :   FD_STORE( ulong, CURSOR, num_bits ); INC( 8U );
     201           0 :   *out_bits_set = (ulong *)(CURSOR)  ; INC( 8U );
     202             : 
     203           0 :   FD_STORE( ulong, CURSOR, mask      ); INC( 8U );
     204           0 :   FD_STORE( uint,  CURSOR, mask_bits ); INC( 4U );
     205             : 
     206           0 :   if( FD_UNLIKELY( BYTES_REMAINING<contact_info_crds_sz )) {
     207           0 :     FD_LOG_WARNING(( "Not enough space in pull request for contact info, check bloom filter params" ));
     208           0 :     return -1;
     209           0 :   }
     210           0 :   fd_memcpy( CURSOR, contact_info_crds, contact_info_crds_sz );
     211           0 :   INC( contact_info_crds_sz );
     212           0 :   *out_payload_sz = BYTES_CONSUMED;
     213           0 :   return 0;
     214           0 : }
     215             : 
     216             : int
     217             : fd_gossip_contact_info_encode( fd_contact_info_t const * contact_info,
     218             :                                uchar *                   out_buf,
     219             :                                ulong                     out_buf_sz,
     220           3 :                                ulong *                   opt_encoded_sz ) {
     221           3 :   FD_TEST( out_buf_sz<=FD_GOSSIP_MTU );
     222             :   /* fd_contact_info_t has a fixed-size array of addresses and sockets, while
     223             :      the encoded representation is a variable-length array of addrs and
     224             :      sockets, where sockets are sorted by port offsets and index into addrs
     225             :      to specify address. */
     226           3 :   uint           addrs[ FD_CONTACT_INFO_SOCKET_MAX ];
     227           3 :   uchar          addrs_cnt;
     228           3 :   socket_entry_t socket_entries[ FD_CONTACT_INFO_SOCKET_MAX ];
     229           3 :   uchar          socket_entries_cnt;
     230             : 
     231           3 :   if( FD_UNLIKELY( contact_info_convert_sockets( contact_info, socket_entries, &socket_entries_cnt, addrs, &addrs_cnt ) ) ) {
     232           0 :     FD_LOG_ERR(( "Failed to convert contact info sockets, check arguments to fd_contact_info_convert_sockets" ));
     233           0 :   }
     234             : 
     235           3 :   SER_INIT( out_buf, out_buf_sz, 0U );
     236             : 
     237           3 :   INC( 64U ); /* Reserve space for signature */
     238             : 
     239           3 :   FD_STORE( uint, CURSOR, FD_GOSSIP_VALUE_CONTACT_INFO ) ; INC(  4U );
     240           3 :   fd_memcpy( CURSOR, contact_info->pubkey.uc, 32UL )        ; INC( 32U );
     241             : 
     242           3 :   ulong wallclock = (ulong)FD_NANOSEC_TO_MILLI( contact_info->wallclock_nanos );
     243           3 :   INC( varint_encode( wallclock, CURSOR ) );
     244             : 
     245           3 :   ulong instance_creation_wallclock = (ulong)FD_NANOSEC_TO_MICRO( contact_info->instance_creation_wallclock_nanos );
     246           3 :   FD_STORE( ulong,  CURSOR, instance_creation_wallclock ); INC( 8UL );
     247           3 :   FD_STORE( ushort, CURSOR, contact_info->shred_version ); INC( 2UL );
     248             : 
     249           3 :   INC( encode_version( contact_info, out_buf, out_buf_sz, CUR_OFFSET ) );
     250             : 
     251             :   /* Encode addrs and socket entries. Properties exploited:
     252             :      - length of either array never exceeds FD_GOSSIP_SOCKET_TAG_MAX, which
     253             :        is 13 < 2^7. This means we can assume the length is always encoded as
     254             :        a single byte varint */
     255           3 :   FD_STORE( uchar, CURSOR, addrs_cnt )                           ; INC( 1UL );
     256           6 :   for( ulong j=0UL; j<addrs_cnt; j++ ) {
     257             :     /* Each address is 8 bytes including discriminant */
     258           3 :     FD_STORE( uint, CURSOR, 0U )                                 ; INC( 4UL ); /* Enum discriminant */
     259           3 :     FD_STORE( uint, CURSOR, addrs[j] )                           ; INC( 4UL );
     260           3 :   }
     261             : 
     262           3 :   FD_STORE( uchar, CURSOR, socket_entries_cnt )                  ; INC( 1UL );
     263             : 
     264           6 :   for( ulong j=0UL; j<socket_entries_cnt; j++ ) {
     265           3 :     FD_STORE( uchar, CURSOR, socket_entries[j].tag )             ; INC( 1UL );
     266           3 :     FD_STORE( uchar, CURSOR, socket_entries[j].addr_index )      ; INC( 1UL );
     267             : 
     268           3 :     INC( varint_encode( socket_entries[j].port_offset, CURSOR ) );
     269           3 :   }
     270             : 
     271             :   /* No extensions for now, but because of a quirk in short_vec we need to encode
     272             :      the length (which is 0) */
     273           3 :   FD_STORE( uchar, CURSOR, 0U )                                  ; INC( 1UL );
     274             : 
     275           3 :   if( opt_encoded_sz ) {
     276           3 :     *opt_encoded_sz = BYTES_CONSUMED;
     277           3 :   }
     278           3 :   return 0;
     279           3 : }
     280             : 
     281             : int
     282             : fd_gossip_crds_vote_encode( uchar *                       out_buf,
     283             :                             ulong                         out_buf_sz,
     284             :                             uchar const *                 txn,
     285             :                             ulong                         txn_sz,
     286             :                             uchar const *                 identity_pubkey,
     287             :                             long                          now,
     288             :                             uchar                         vote_index,
     289           3 :                             fd_gossip_view_crds_value_t * out_view ) {
     290           3 :   fd_gossip_view_vote_t * vote = out_view->vote;
     291             : 
     292           3 :   SER_INIT( out_buf, out_buf_sz, 0U );
     293           3 :   out_view->signature_off = CUR_OFFSET          ; /* Reserve space for signature */              ; INC( 64U );
     294           3 :   out_view->tag           = FD_GOSSIP_VALUE_VOTE; FD_STORE( uint,  CURSOR, FD_GOSSIP_VALUE_VOTE ); INC(  4U );
     295             : 
     296           3 :   vote->index               = vote_index; FD_STORE ( uchar, CURSOR, vote_index )                       ; INC(  1U );
     297           3 :   out_view->pubkey_off      = CUR_OFFSET; fd_memcpy( CURSOR, identity_pubkey, 32UL )                   ; INC( 32U );
     298           3 :   vote->txn_off             = CUR_OFFSET; fd_memcpy( CURSOR, txn, txn_sz )                             ; INC( (ushort)txn_sz );
     299           3 :   out_view->wallclock_nanos = now       ; FD_STORE ( ulong, CURSOR, (ulong)FD_NANOSEC_TO_MILLI( now ) ); INC(  8U );
     300             : 
     301           3 :   vote->txn_sz     = (ushort)txn_sz;
     302           3 :   out_view->length = (ushort)BYTES_CONSUMED;
     303           3 :   return 0;
     304           3 : }

Generated by: LCOV version 1.14