LCOV - code coverage report
Current view: top level - flamenco/gossip - fd_gossip_msg_ser.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 154 0.0 %
Date: 2025-09-19 04:41:14 Functions: 0 6 0.0 %

          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           0 :   uchar *       _payload    = (payload);        \
       7           0 :   ulong const   _offset     = (offset);         \
       8           0 :   ulong         _i          = (offset);         \
       9           0 :   ulong const   _payload_sz = (payload_sz);     \
      10           0 :   (void)        _offset;                        \
      11           0 :   (void)        _payload_sz;
      12             : 
      13           0 : #define INC( n ) (_i += (ulong)(n))
      14             : 
      15             : #define CUR_OFFSET      ((ushort)_i)
      16           0 : #define CURSOR          (_payload+_i)
      17           0 : #define BYTES_CONSUMED  (_i-_offset)
      18             : #define BYTES_REMAINING (_payload_sz-_i)
      19             : 
      20             : static inline ushort
      21           0 : varint_encode( ulong u64, uchar * out_buf ) {
      22           0 :   ushort i = 0UL;
      23           0 :   do {
      24           0 :     uchar byte = (uchar)(u64 & 0x7FUL);
      25           0 :     u64 >>= 7UL;
      26           0 :     if( u64 ) byte |= 0x80U;
      27           0 :     FD_STORE( uchar, out_buf+i, byte );
      28           0 :     i++;
      29           0 :   } while( u64 );
      30           0 :   return i;
      31           0 : }
      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           0 :                 ushort                    start_offset ) {
      38           0 :   SER_INIT( out_buf, out_buf_sz, start_offset );
      39             : 
      40           0 :   INC( varint_encode( contact_info->version.major, CURSOR ) );
      41           0 :   INC( varint_encode( contact_info->version.minor, CURSOR ) );
      42           0 :   INC( varint_encode( contact_info->version.patch, CURSOR ) );
      43             : 
      44           0 :   FD_STORE( uint, CURSOR, contact_info->version.commit )           ; INC( 4U );
      45             : 
      46           0 :   FD_STORE( uint, CURSOR, contact_info->version.feature_set )      ; INC( 4U );
      47             : 
      48           0 :   INC( varint_encode( contact_info->version.client, CURSOR ) );
      49             : 
      50           0 :   return BYTES_CONSUMED;
      51           0 : }
      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           0 : #define FD_CONTACT_INFO_SOCKET_MAX (FD_CONTACT_INFO_SOCKET_LAST+1UL)
     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           0 :                               uchar *                               out_addrs_cnt ) {
     111           0 :   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           0 :   socket_ctx_t filled_up[ FD_CONTACT_INFO_SOCKET_MAX ];
     117           0 :   ulong filled_up_cnt = 0UL;
     118           0 :   for( ulong j=0; j<FD_CONTACT_INFO_SOCKET_MAX; j++ ) {
     119           0 :     if( contact_info->sockets[j].l!=0 ){
     120           0 :       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           0 :       filled_up[filled_up_cnt].socket.port = fd_ushort_bswap( filled_up[filled_up_cnt].socket.port );
     124           0 :       filled_up[filled_up_cnt].socket_tag = (uchar)j;
     125           0 :       filled_up_cnt++;
     126           0 :     }
     127           0 :   }
     128             : 
     129           0 :   socket_ctx_t scratch[ FD_CONTACT_INFO_SOCKET_MAX ];
     130           0 :   socket_ctx_t * sorted = sort_socket_port_stable_fast( filled_up, filled_up_cnt, scratch );
     131             : 
     132           0 :   uchar addrs_cnt = 0UL;
     133           0 :   uchar socket_entries_cnt = 0UL;
     134             : 
     135             :   /* fill in first entry */
     136           0 :   out_addrs[addrs_cnt++]                              = sorted[0].socket.addr;
     137           0 :   out_sockets_entries[socket_entries_cnt].port_offset = sorted[0].socket.port;
     138           0 :   out_sockets_entries[socket_entries_cnt].addr_index  = 0U;
     139           0 :   out_sockets_entries[socket_entries_cnt++].tag       = sorted[0].socket_tag;
     140             : 
     141           0 :   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           0 :   *out_addrs_cnt              = addrs_cnt;
     164           0 :   *out_socket_entries_cnt     = socket_entries_cnt;
     165           0 :   return 0;
     166           0 : }
     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           0 :                                ulong *                       opt_encoded_sz ) {
     221           0 :   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           0 :   uint           addrs[ FD_CONTACT_INFO_SOCKET_MAX ];
     227           0 :   uchar          addrs_cnt;
     228           0 :   socket_entry_t socket_entries[ FD_CONTACT_INFO_SOCKET_MAX ];
     229           0 :   uchar          socket_entries_cnt;
     230             : 
     231           0 :   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           0 :   SER_INIT( out_buf, out_buf_sz, 0U );
     236             : 
     237           0 :   INC( 64U ); /* Reserve space for signature */
     238             : 
     239           0 :   FD_STORE( uint, CURSOR, FD_GOSSIP_VALUE_CONTACT_INFO ) ; INC(  4U );
     240           0 :   fd_memcpy( CURSOR, contact_info->pubkey.uc, 32UL )        ; INC( 32U );
     241             : 
     242           0 :   ulong wallclock = (ulong)FD_NANOSEC_TO_MILLI( contact_info->wallclock_nanos );
     243           0 :   INC( varint_encode( wallclock, CURSOR ) );
     244             : 
     245           0 :   ulong instance_creation_wallclock = (ulong)FD_NANOSEC_TO_MICRO( contact_info->instance_creation_wallclock_nanos );
     246           0 :   FD_STORE( ulong,  CURSOR, instance_creation_wallclock ); INC( 8UL );
     247           0 :   FD_STORE( ushort, CURSOR, contact_info->shred_version ); INC( 2UL );
     248             : 
     249           0 :   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           0 :   FD_STORE( uchar, CURSOR, addrs_cnt )                           ; INC( 1UL );
     256           0 :   for( ulong j=0UL; j<addrs_cnt; j++ ) {
     257             :     /* Each address is 8 bytes including discriminant */
     258           0 :     FD_STORE( uint, CURSOR, 0U )                                 ; INC( 4UL ); /* Enum discriminant */
     259           0 :     FD_STORE( uint, CURSOR, addrs[j] )                           ; INC( 4UL );
     260           0 :   }
     261             : 
     262           0 :   FD_STORE( uchar, CURSOR, socket_entries_cnt )                  ; INC( 1UL );
     263             : 
     264           0 :   for( ulong j=0UL; j<socket_entries_cnt; j++ ) {
     265           0 :     FD_STORE( uchar, CURSOR, socket_entries[j].tag )             ; INC( 1UL );
     266           0 :     FD_STORE( uchar, CURSOR, socket_entries[j].addr_index )      ; INC( 1UL );
     267             : 
     268           0 :     INC( varint_encode( socket_entries[j].port_offset, CURSOR ) );
     269           0 :   }
     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           0 :   FD_STORE( uchar, CURSOR, 0U )                                  ; INC( 1UL );
     274             : 
     275           0 :   if( opt_encoded_sz ) {
     276           0 :     *opt_encoded_sz = BYTES_CONSUMED;
     277           0 :   }
     278           0 :   return 0;
     279           0 : }
     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           0 :                             ulong *       opt_encoded_sz ) {
     289           0 :   SER_INIT( out_buf, out_buf_sz, 0U );
     290           0 :   INC( 64U ); /* Reserve space for signature */
     291             : 
     292           0 :   FD_STORE( uint,  CURSOR, FD_GOSSIP_VALUE_VOTE )             ; INC( 4U );
     293           0 :   FD_STORE( uchar, CURSOR, 0U )                               ; INC( 1U ); /* TODO: vote tower index, unused for now */
     294           0 :   fd_memcpy( CURSOR, identity_pubkey, 32UL )                  ; INC( 32U );
     295           0 :   fd_memcpy( CURSOR, txn,             txn_sz )                ; INC( (ushort)txn_sz );
     296           0 :   FD_STORE( ulong, CURSOR, (ulong)FD_NANOSEC_TO_MILLI( now ) ); INC( 8U );
     297             : 
     298           0 :   if( opt_encoded_sz ) {
     299           0 :     *opt_encoded_sz = BYTES_CONSUMED; /* Return the size of the encoded vote */
     300           0 :   }
     301           0 :   return 0;
     302           0 : }

Generated by: LCOV version 1.14