LCOV - code coverage report
Current view: top level - flamenco/gossip - fd_gossip_message.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 475 689 68.9 %
Date: 2026-10-22 18:10:43 Functions: 21 27 77.8 %

          Line data    Source code
       1             : #include "fd_gossip_message.h"
       2             : 
       3             : #include <string.h>
       4             : 
       5             : #include "../../ballet/txn/fd_compact_u16.h"
       6             : #include "../runtime/fd_system_ids.h"
       7             : #include "../runtime/program/vote/fd_vote_codec.h"
       8             : 
       9             : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/gossip/src/crds_data.rs#L22-L23 */
      10             : #define WALLCLOCK_MAX_MILLIS (1000000000000000UL)
      11             : #define MAX_SLOT             (1000000000000000UL)
      12             : 
      13             : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/gossip/src/epoch_slots.rs#L16 */
      14             : #define MAX_SLOTS_PER_EPOCH_SLOT (2048UL*8UL)
      15             : 
      16             : #define FD_GOSSIP_VOTE_IDX_MAX (32)
      17             : #define FD_GOSSIP_EPOCH_SLOTS_IDX_MAX (255U)
      18             : #define FD_GOSSIP_DUPLICATE_SHRED_IDX_MAX (512U)
      19             : 
      20             : /* Agave computes this threshold as:
      21             :  *   mask_bits( MIN_NUM_BLOOM_ITEMS, max_items( PACKET_DATA_SIZE*8, FALSE_RATE, KEYS ) )
      22             :  * where mask_bits(n, m) = ceil(log2(n/m)). The derivation uses all protocol
      23             :  * constants so the result (6) is a protocol constant too.
      24             :  * https://github.com/anza-xyz/agave/blob/v4.2.0-beta.0/gossip/src/crds_gossip_pull.rs#L71-L79 */
      25             : #define FD_GOSSIP_MIN_PULL_REQUEST_MASK_BITS (6U)
      26             : 
      27       83434 : #define CHECK( cond ) do {               \
      28       83410 :   if( FD_UNLIKELY( !(cond) ) ) return 0; \
      29       83410 : } while( 0 )
      30             : 
      31       20748 : #define READ_BYTES( dst, n, payload, payload_sz ) do { \
      32       20748 :   CHECK( (n)<=(*(payload_sz)) );                       \
      33       20748 :   fd_memcpy( (dst), *(payload), (n) );                 \
      34       20708 :   *(payload) += (n);                                   \
      35       20708 :   *(payload_sz) -= (n);                                \
      36       20708 : } while( 0 )
      37             : 
      38         373 : #define SKIP_BYTES( n, payload, payload_sz ) do { \
      39         373 :   CHECK( (n)<=(*(payload_sz)) );                  \
      40         373 :   *(payload) += (n);                              \
      41         372 :   *(payload_sz) -= (n);                           \
      42         372 : } while( 0 )
      43             : 
      44         633 : #define READ_OPTION( dst, payload, payload_sz ) do { \
      45         633 :   READ_U8( dst, payload, payload_sz );               \
      46         633 :   CHECK( (dst)==0 || (dst)==1 );                     \
      47         633 : } while( 0 )
      48             : 
      49        6946 : #define READ_ENUM( dst, n, payload, payload_sz ) do { \
      50        6946 :   CHECK( 4UL<=(*(payload_sz)) );                      \
      51        6946 :   (dst) = FD_LOAD( uint, *(payload) );                \
      52        6946 :   CHECK( (dst)<n );                                   \
      53        6946 :   *(payload) += 4UL;                                  \
      54        6440 :   *(payload_sz) -= 4UL;                               \
      55        6440 : } while( 0 )
      56             : 
      57        1566 : #define READ_U8( dst, payload, payload_sz ) do { \
      58        1566 :   CHECK( 1UL<=(*(payload_sz)) );                 \
      59        1566 :   (dst) = FD_LOAD( uchar, *(payload) );          \
      60        1566 :   *(payload) += 1UL;                             \
      61        1566 :   *(payload_sz) -= 1UL;                          \
      62        1566 : } while( 0 )
      63             : 
      64         158 : #define READ_U16( dst, payload, payload_sz ) do { \
      65         158 :   CHECK( 2UL<=(*(payload_sz)) );                  \
      66         158 :   (dst) = FD_LOAD( ushort, *(payload) );          \
      67         158 :   *(payload) += 2UL;                              \
      68         158 :   *(payload_sz) -= 2UL;                           \
      69         158 : } while( 0 )
      70             : 
      71         235 : #define READ_U32( dst, payload, payload_sz ) do { \
      72         235 :   CHECK( 4UL<=(*(payload_sz)) );                  \
      73         235 :   (dst) = FD_LOAD( uint, *(payload) );            \
      74         235 :   *(payload) += 4UL;                              \
      75         235 :   *(payload_sz) -= 4UL;                           \
      76         235 : } while( 0 )
      77             : 
      78       27115 : #define READ_U64( dst, payload, payload_sz ) do { \
      79       27115 :   CHECK( 8UL<=(*(payload_sz)) );                  \
      80       27115 :   (dst) = FD_LOAD( ulong, *(payload) );           \
      81       26858 :   *(payload) += 8UL;                              \
      82       26858 :   *(payload_sz) -= 8UL;                           \
      83       26858 : } while( 0 )
      84             : 
      85         988 : #define READ_U16_VARINT( dst, payload, payload_sz ) do {   \
      86         988 :   ulong _sz = fd_cu16_dec_sz( *(payload), *(payload_sz) ); \
      87         988 :   CHECK( _sz );                                            \
      88         988 :   (dst) = fd_cu16_dec_fixed( *(payload), _sz );            \
      89         971 :   *(payload) += _sz;                                       \
      90         971 :   *(payload_sz) -= _sz;                                    \
      91         971 : } while( 0 )
      92             : 
      93          84 : #define READ_U64_VARINT( dst, payload, payload_sz ) do {                       \
      94          84 :   ulong _val = 0UL;                                                            \
      95          84 :   uint  _shift = 0U;                                                           \
      96         467 :   for(;;) {                                                                    \
      97         467 :     CHECK( 1UL<=(*(payload_sz)) );                                             \
      98         467 :     uchar _byte = FD_LOAD( uchar, *(payload) );                                \
      99         467 :     *(payload) += 1UL;                                                         \
     100         467 :     *(payload_sz) -= 1UL;                                                      \
     101         467 :     _val |= (ulong)(_byte & 0x7F) << _shift;                                   \
     102         467 :     if( FD_LIKELY( !(_byte & 0x80) ) ) {                                       \
     103          84 :       CHECK( (_val>>_shift)==(ulong)_byte );     /* last byte not truncated */ \
     104          84 :       CHECK( _byte || !_shift );                 /* no trailing zero bytes */  \
     105          84 :       (dst) = _val;                                                            \
     106          84 :       break;                                                                   \
     107          84 :     }                                                                          \
     108         467 :     _shift += 7U;                                                              \
     109         383 :     CHECK( _shift<64U );                                                       \
     110         383 :   }                                                                            \
     111          84 : } while( 0 )
     112             : 
     113        1009 : #define READ_WALLCLOCK( dst, payload, payload_sz ) do { \
     114        1009 :   ulong wallclock_millis;                               \
     115        1009 :   READ_U64( wallclock_millis, payload, payload_sz );    \
     116        1009 :   CHECK( wallclock_millis<WALLCLOCK_MAX_MILLIS );       \
     117        1008 :   (dst) = wallclock_millis;                             \
     118         967 : } while( 0 )
     119             : 
     120             : static int
     121             : deser_vote_instruction( uchar const * data,
     122          52 :                         ulong         data_len ) {
     123          52 :   fd_vote_instruction_t vote_instruction[1];
     124          52 :   CHECK( fd_vote_instruction_deserialize( vote_instruction, data, data_len ) );
     125          52 :   CHECK(
     126          52 :     vote_instruction->discriminant==fd_vote_instruction_enum_vote ||
     127          52 :     vote_instruction->discriminant==fd_vote_instruction_enum_vote_switch ||
     128          52 :     vote_instruction->discriminant==fd_vote_instruction_enum_update_vote_state ||
     129          52 :     vote_instruction->discriminant==fd_vote_instruction_enum_update_vote_state_switch ||
     130          52 :     vote_instruction->discriminant==fd_vote_instruction_enum_compact_update_vote_state  ||
     131          52 :     vote_instruction->discriminant==fd_vote_instruction_enum_compact_update_vote_state_switch  ||
     132          52 :     vote_instruction->discriminant==fd_vote_instruction_enum_tower_sync  ||
     133          52 :     vote_instruction->discriminant==fd_vote_instruction_enum_tower_sync_switch );
     134             :   // Oddly, trailing garbage is allowed here at the end of the instruction
     135          52 :   return 1;
     136          52 : }
     137             : 
     138             : static int
     139             : deser_vote_txn( fd_gossip_vote_t * vote,
     140             :                 uchar const **     payload,
     141          53 :                 ulong *            payload_sz ) {
     142          53 :   uchar const * payload_start = *payload;
     143             : 
     144          53 :   ushort signatures_len;
     145          53 :   READ_U16_VARINT( signatures_len, payload, payload_sz );
     146          53 :   SKIP_BYTES( signatures_len*64UL, payload, payload_sz );
     147          52 :   uchar num_required_signatures, num_readonly_signed_accounts, num_readonly_unsigned_accounts;
     148          52 :   READ_U8( num_required_signatures, payload, payload_sz );
     149          52 :   READ_U8( num_readonly_signed_accounts, payload, payload_sz );
     150          52 :   READ_U8( num_readonly_unsigned_accounts, payload, payload_sz );
     151          52 :   ushort account_keys_len;
     152          52 :   READ_U16_VARINT( account_keys_len, payload, payload_sz );
     153          52 :   uchar const * account_keys = *payload;
     154          52 :   SKIP_BYTES( account_keys_len*32UL, payload, payload_sz );
     155          52 :   SKIP_BYTES( 32UL, payload, payload_sz ); /* recent blockhash */
     156          52 :   ushort instructions_len;
     157          52 :   READ_U16_VARINT( instructions_len, payload, payload_sz );
     158         104 :   for( ulong i=0UL; i<instructions_len; i++ ) {
     159          52 :     uchar program_id_index;
     160          52 :     READ_U8( program_id_index, payload, payload_sz );
     161          52 :     CHECK( program_id_index<account_keys_len );
     162          52 :     CHECK( program_id_index );
     163          52 :     ushort accounts_len;
     164          52 :     READ_U16_VARINT( accounts_len, payload, payload_sz );
     165         163 :     for( ulong j=0UL; j<accounts_len; j++ ) {
     166         111 :       uchar account_index;
     167         111 :       READ_U8( account_index, payload, payload_sz );
     168         111 :       CHECK( account_index<account_keys_len );
     169         111 :     }
     170          52 :     ushort data_len;
     171          52 :     READ_U16_VARINT( data_len, payload, payload_sz );
     172          52 :     uchar data[ 1232UL ];
     173          52 :     READ_BYTES( data, data_len, payload, payload_sz );
     174          52 :     if( FD_LIKELY( i==0UL ) ) {
     175          52 :       CHECK( accounts_len );
     176          52 :       uchar const * account_key = account_keys+32UL*program_id_index;
     177          52 :       CHECK( !memcmp( account_key, fd_solana_vote_program_id.uc, 32UL ) );
     178          52 :       CHECK( deser_vote_instruction( data, data_len ) );
     179          52 :     }
     180          52 :   }
     181             : 
     182          52 :   CHECK( num_required_signatures<=signatures_len );
     183          52 :   CHECK( signatures_len<=account_keys_len );
     184          52 :   CHECK( num_required_signatures+num_readonly_unsigned_accounts<=account_keys_len );
     185          52 :   CHECK( num_readonly_signed_accounts<num_required_signatures );
     186          52 :   CHECK( instructions_len );
     187             : 
     188          52 :   vote->transaction_len = (ulong)(*payload-payload_start);
     189          52 :   fd_memcpy( vote->transaction, payload_start, vote->transaction_len );
     190          52 :   return 1;
     191          52 : }
     192             : 
     193             : static int
     194             : deser_vote( fd_gossip_value_t * value,
     195             :             uchar const **      payload,
     196          75 :             ulong *             payload_sz ) {
     197          75 :   READ_U8( value->vote->index, payload, payload_sz );
     198          75 :   CHECK( value->vote->index<FD_GOSSIP_VOTE_IDX_MAX );
     199          53 :   READ_BYTES( value->origin, 32UL, payload, payload_sz );
     200             : 
     201          53 :   CHECK( deser_vote_txn( value->vote, payload, payload_sz ) );
     202          52 :   READ_WALLCLOCK( value->wallclock, payload, payload_sz );
     203          52 :   return 1;
     204          52 : }
     205             : 
     206             : static int
     207             : deser_lowest_slot( fd_gossip_value_t * value,
     208             :                    uchar const **      payload,
     209          94 :                    ulong *             payload_sz ) {
     210          94 :   uchar ix;
     211          94 :   READ_U8( ix, payload, payload_sz );
     212          94 :   CHECK( !ix );
     213          63 :   READ_BYTES( value->origin, 32UL, payload, payload_sz );
     214          63 :   ulong root;
     215          63 :   READ_U64( root, payload, payload_sz );
     216          63 :   CHECK( !root );
     217          63 :   READ_U64( value->lowest_slot->lowest, payload, payload_sz );
     218          63 :   CHECK( value->lowest_slot->lowest<MAX_SLOT );
     219          63 :   ulong slots_len;
     220          63 :   READ_U64( slots_len, payload, payload_sz );
     221          63 :   CHECK( !slots_len );
     222          63 :   ulong stash_len;
     223          63 :   READ_U64( stash_len, payload, payload_sz );
     224          63 :   CHECK( !stash_len );
     225          63 :   READ_WALLCLOCK( value->wallclock, payload, payload_sz );
     226          63 :   return 1;
     227          63 : }
     228             : 
     229             : static int
     230             : deser_bitvec_u8_epoch_slots( uchar const ** payload,
     231          46 :                              ulong *        payload_sz ) {
     232          46 :   uchar has_bits;
     233          46 :   READ_OPTION( has_bits, payload, payload_sz );
     234          46 :   if( FD_UNLIKELY( !has_bits ) ) {
     235          16 :     ulong bits_cnt;
     236          16 :     READ_U64( bits_cnt, payload, payload_sz );
     237          16 :     CHECK( !bits_cnt );
     238          15 :     return 1;
     239          16 :   }
     240             : 
     241          30 :   ulong bits_cap;
     242          30 :   READ_U64( bits_cap, payload, payload_sz );
     243          30 :   SKIP_BYTES( bits_cap, payload, payload_sz );
     244          30 :   ulong bits_cnt;
     245          30 :   READ_U64( bits_cnt, payload, payload_sz );
     246          30 :   CHECK( bits_cnt==bits_cap*8UL );
     247          27 :   return 1;
     248          30 : }
     249             : 
     250             : static int
     251             : deser_epoch_slots( fd_gossip_value_t * value,
     252             :                    uchar const **      payload,
     253          79 :                    ulong *             payload_sz ) {
     254          79 :   READ_U8( value->epoch_slots->index, payload, payload_sz );
     255          79 :   CHECK( value->epoch_slots->index<FD_GOSSIP_EPOCH_SLOTS_IDX_MAX );
     256          79 :   READ_BYTES( value->origin, 32UL, payload, payload_sz );
     257          79 :   ulong slots_len;
     258          79 :   READ_U64( slots_len, payload, payload_sz );
     259         154 :   for( ulong i=0UL; i<slots_len; i++ ) {
     260         103 :     uint is_uncompressed;
     261         103 :     READ_ENUM( is_uncompressed, 2UL, payload, payload_sz );
     262          79 :     ulong first_slot;
     263          79 :     READ_U64( first_slot, payload, payload_sz );
     264          79 :     CHECK( first_slot<MAX_SLOT );
     265          79 :     ulong num;
     266          79 :     READ_U64( num, payload, payload_sz );
     267          79 :     CHECK( num<MAX_SLOTS_PER_EPOCH_SLOT );
     268          79 :     if( FD_UNLIKELY( is_uncompressed ) ) {
     269          47 :       CHECK( deser_bitvec_u8_epoch_slots( payload, payload_sz ) );
     270          47 :     } else {
     271          32 :       ulong compressed_len;
     272          32 :       READ_U64( compressed_len, payload, payload_sz );
     273          32 :       SKIP_BYTES( compressed_len, payload, payload_sz );
     274          32 :     }
     275          79 :   }
     276          51 :   READ_WALLCLOCK( value->wallclock, payload, payload_sz );
     277          51 :   return 1;
     278          51 : }
     279             : 
     280             : static int
     281             : deser_duplicate_shred( fd_gossip_value_t * value,
     282             :                        uchar const **      payload,
     283          74 :                        ulong *             payload_sz ) {
     284          74 :   READ_U16( value->duplicate_shred->index, payload, payload_sz );
     285          74 :   CHECK( value->duplicate_shred->index<FD_GOSSIP_DUPLICATE_SHRED_IDX_MAX );
     286          46 :   READ_BYTES( value->origin, 32UL, payload, payload_sz );
     287          46 :   READ_WALLCLOCK( value->wallclock, payload, payload_sz );
     288          46 :   READ_U64( value->duplicate_shred->slot, payload, payload_sz );
     289          46 :   SKIP_BYTES( 5UL, payload, payload_sz ); /* (unused) + shred type (unused) */
     290          46 :   READ_U8( value->duplicate_shred->num_chunks, payload, payload_sz );
     291          46 :   READ_U8( value->duplicate_shred->chunk_index, payload, payload_sz );
     292          46 :   CHECK( value->duplicate_shred->chunk_index<value->duplicate_shred->num_chunks );
     293          46 :   READ_U64( value->duplicate_shred->chunk_len, payload, payload_sz );
     294          46 :   READ_BYTES( value->duplicate_shred->chunk, value->duplicate_shred->chunk_len, payload, payload_sz );
     295          46 :   return 1;
     296          46 : }
     297             : 
     298             : static int
     299             : deser_snapshot_hashes( fd_gossip_value_t * value,
     300             :                        uchar const **      payload,
     301          86 :                        ulong *             payload_sz ) {
     302          86 :   READ_BYTES( value->origin, 32UL, payload, payload_sz );
     303          86 :   READ_U64( value->snapshot_hashes->full_slot, payload, payload_sz );
     304          86 :   CHECK( value->snapshot_hashes->full_slot<MAX_SLOT );
     305          61 :   READ_BYTES( value->snapshot_hashes->full_hash, 32UL, payload, payload_sz );
     306          61 :   READ_U64( value->snapshot_hashes->incremental_len, payload, payload_sz );
     307         150 :   for( ulong i=0UL; i<value->snapshot_hashes->incremental_len; i++ ) {
     308          89 :     READ_U64( value->snapshot_hashes->incremental[ i ].slot, payload, payload_sz );
     309          89 :     CHECK( value->snapshot_hashes->incremental[ i ].slot<MAX_SLOT );
     310          89 :     CHECK( value->snapshot_hashes->incremental[ i ].slot>value->snapshot_hashes->full_slot );
     311          89 :     READ_BYTES( value->snapshot_hashes->incremental[ i ].hash, 32UL, payload, payload_sz );
     312          89 :   }
     313          61 :   READ_WALLCLOCK( value->wallclock, payload, payload_sz );
     314          61 :   return 1;
     315          61 : }
     316             : 
     317             : static int
     318             : deser_contact_info( fd_gossip_value_t * value,
     319             :                     uchar const **      payload,
     320          84 :                     ulong *             payload_sz ) {
     321          84 :   READ_BYTES( value->origin, 32UL, payload, payload_sz );
     322          84 :   READ_U64_VARINT( value->wallclock, payload, payload_sz );
     323          84 :   CHECK( value->wallclock<WALLCLOCK_MAX_MILLIS );
     324          84 :   READ_U64( value->contact_info->outset, payload, payload_sz );
     325          84 :   READ_U16( value->contact_info->shred_version, payload, payload_sz );
     326          84 :   READ_U16_VARINT( value->contact_info->version.major, payload, payload_sz );
     327          78 :   READ_U16_VARINT( value->contact_info->version.minor, payload, payload_sz );
     328          75 :   READ_U16_VARINT( value->contact_info->version.patch, payload, payload_sz );
     329          70 :   READ_U32( value->contact_info->version.commit, payload, payload_sz );
     330          70 :   READ_U32( value->contact_info->version.feature_set, payload, payload_sz );
     331          70 :   READ_U16_VARINT( value->contact_info->version.client, payload, payload_sz );
     332             : 
     333             :   /* Tightest bounds for array sizes given network constraints.
     334             : 
     335             :      IPv6 minimum MTU             = 1280
     336             :      IPv6 header                  =   40
     337             :      UDP header                   =    8
     338             :      PACKET_DATA_SIZE             = 1232   (= 1280 - 40 - 8)
     339             : 
     340             :      Bytes consumed for preamble:
     341             :        Push/Pull Response:
     342             :          Protocol tag(4) + from(32) + values_len(8) = 44
     343             :        Pull Request:
     344             :          Protocol tag(4) + keys_len(8) + bloom_none(9) +
     345             :          num_bits_set(8) + mask(8) + mask_bits(4) = 41
     346             :        Minimum consumed for preamble: 41
     347             : 
     348             :      Bytes consumed before addrs loop:
     349             :        Preamble(41) + signature(64) +
     350             :        CrdsData tag(4) + origin(32) + wallclock_varint(1) + outset(8) +
     351             :        shred_version(2) + major(1) + minor(1) + patch(1) + commit(4) +
     352             :        feature_set(4) + client(1) + addrs_len_varint(1)             = 165
     353             : 
     354             :      Remaining: 1232 - 165 = 1067
     355             :      Each addr: READ_ENUM(4) + READ_U32(4) = 8 bytes minimum
     356             :      Max addrs = floor(1067/8) = 133
     357             : 
     358             :      Bytes consumed before sockets loop:
     359             :        (same as above) + sockets_len_varint(1)                     = 166
     360             : 
     361             :      Remaining: 1232 - 166 = 1066
     362             :      Each socket: READ_U8(1) + READ_U8(1) + READ_U16_VARINT(1) = 3 bytes minimum
     363             :      Max sockets = floor(1066/3) = 355 */
     364             : 
     365          67 : #define FD_GOSSIP_CONTACT_INFO_MAX_ADDRESSES (133UL)
     366          67 : #define FD_GOSSIP_CONTACT_INFO_MAX_SOCKETS   (355UL)
     367             : 
     368          67 :   uint is_ip6[ FD_GOSSIP_CONTACT_INFO_MAX_ADDRESSES ];
     369          67 :   union {
     370          67 :     uint ip4;
     371          67 :     uchar ip6[ 16UL ];
     372          67 :   } ips[ FD_GOSSIP_CONTACT_INFO_MAX_ADDRESSES ];
     373             : 
     374          67 :   ulong addrs_len;
     375          67 :   READ_U16_VARINT( addrs_len, payload, payload_sz );
     376         204 :   for( ulong i=0UL; i<addrs_len; i++ ) {
     377         147 :     READ_ENUM( is_ip6[ i ], 2UL, payload, payload_sz );
     378         137 :     if( !is_ip6[ i ] ) READ_U32( ips[ i ].ip4, payload, payload_sz );
     379          49 :     else               READ_BYTES( ips[ i ].ip6, 16UL, payload, payload_sz );
     380         137 :   }
     381             : 
     382          57 :   struct {
     383          57 :     uchar  key;
     384          57 :     uchar  index;
     385          57 :     ushort offset;
     386          57 :   } sockets[ FD_GOSSIP_CONTACT_INFO_MAX_SOCKETS ];
     387             : 
     388          57 :   ulong sockets_len;
     389          57 :   READ_U16_VARINT( sockets_len, payload, payload_sz );
     390         194 :   for( ulong i=0UL; i<sockets_len; i++ ) {
     391         137 :     READ_U8( sockets[ i ].key, payload, payload_sz );
     392         137 :     READ_U8( sockets[ i ].index, payload, payload_sz );
     393         137 :     READ_U16_VARINT( sockets[ i ].offset, payload, payload_sz );
     394         137 :   }
     395             : 
     396          57 :   ulong extensions_len;
     397          57 :   READ_U16_VARINT( extensions_len, payload, payload_sz );
     398          57 :   for( ulong i=0UL; i<extensions_len; i++ ) {
     399           0 :     SKIP_BYTES( 1UL, payload, payload_sz ); /* type */
     400           0 :     ushort bytes_len;
     401           0 :     READ_U16_VARINT( bytes_len, payload, payload_sz );
     402           0 :     SKIP_BYTES( bytes_len, payload, payload_sz );
     403           0 :   }
     404             : 
     405             :   /* Ipv6 and duplicate IPs are not allowed
     406             :      https://github.com/anza-xyz/agave/blob/v4.2.0-beta.0/gossip/src/contact_info.rs#L667-L676 */
     407         127 :   for( ulong i=0UL; i<addrs_len; i++ ) {
     408         103 :     CHECK( !is_ip6[ i ] );
     409         113 :     for( ulong j=0UL; j<i; j++ ) {
     410          43 :       CHECK( ips[ i ].ip4!=ips[ j ].ip4 );
     411          43 :     }
     412          70 :   }
     413             : 
     414             :   /* Each socket must reference unique key */
     415          24 :   int seen_socket_key[ 256UL ] = {0};
     416          68 :   for( ulong i=0UL; i<sockets_len; i++ ) {
     417          44 :     CHECK( !seen_socket_key[ sockets[ i ].key ] );
     418          44 :     seen_socket_key[ sockets[ i ].key ] = 1;
     419          44 :   }
     420             : 
     421             :   /* Each IP address must be referenced by at least one socket */
     422          24 :   int seen_ip_addr[ FD_GOSSIP_CONTACT_INFO_MAX_ADDRESSES ] = {0};
     423          68 :   for( ulong i=0UL; i<sockets_len; i++ ) {
     424          44 :     CHECK( sockets[ i ].index<addrs_len );
     425          44 :     seen_ip_addr[ sockets[ i ].index ] = 1;
     426          44 :   }
     427          68 :   for( ulong i=0UL; i<addrs_len; i++ ) CHECK( seen_ip_addr[ i ] );
     428             : 
     429             :   /* Port offsets don't overflow */
     430          24 :   ushort cur_port = 0U;
     431          68 :   for( ulong i=0UL; i<sockets_len; i++ ) {
     432          44 :     ushort result;
     433          44 :     CHECK( !__builtin_add_overflow( cur_port, sockets[ i ].offset, &result ) );
     434          44 :     cur_port = result;
     435          44 :   }
     436             : 
     437          24 :   memset( value->contact_info->sockets, 0, sizeof( value->contact_info->sockets ) );
     438             : 
     439          24 :   cur_port = 0U;
     440          68 :   for( ulong i=0UL; i<sockets_len; i++ ) {
     441          44 :     cur_port = (ushort)(cur_port + sockets[ i ].offset);
     442          44 :     if( FD_LIKELY( sockets[ i ].key<FD_GOSSIP_CONTACT_INFO_SOCKET_CNT ) ) {
     443          44 :       value->contact_info->sockets[ sockets[ i ].key ].is_ipv6 = is_ip6[ sockets[ i ].index ];
     444          44 :       if( FD_LIKELY( !is_ip6[ sockets[ i ].index ] ) ) value->contact_info->sockets[ sockets[ i ].key ].ip4 = ips[ sockets[ i ].index ].ip4;
     445           0 :       else                                             fd_memcpy( value->contact_info->sockets[ sockets[ i ].key ].ip6, ips[ sockets[ i ].index ].ip6, 16UL );
     446             : 
     447          44 :       value->contact_info->sockets[ sockets[ i ].key ].port = fd_ushort_bswap( cur_port );
     448          44 :     }
     449          44 :   }
     450          24 :   return 1;
     451          24 : }
     452             : 
     453             : /* https://github.com/anza-xyz/wincode/blob/wincode%40v0.6.1/wincode/src/schema/external/bv.rs#L89-L147 */
     454             : static int
     455             : deser_bitvec_u8_restart_last_voted_fork_slots( uchar const ** payload,
     456          25 :                                                ulong *        payload_sz ) {
     457          25 :   uchar has_bits;
     458          25 :   READ_OPTION( has_bits, payload, payload_sz );
     459          25 :   if( FD_UNLIKELY( !has_bits ) ) {
     460          12 :     ulong bits_len;
     461          12 :     READ_U64( bits_len, payload, payload_sz );
     462          12 :     CHECK( !bits_len );
     463          12 :     return 1;
     464          12 :   }
     465             : 
     466          13 :   ulong bits_cap;
     467          13 :   READ_U64( bits_cap, payload, payload_sz );
     468          13 :   SKIP_BYTES( bits_cap, payload, payload_sz );
     469          13 :   ulong bits_len;
     470          13 :   READ_U64( bits_len, payload, payload_sz );
     471          13 :   CHECK( bits_len<=bits_cap*8UL );
     472          13 :   return 1;
     473          13 : }
     474             : 
     475             : static int
     476             : deser_restart_last_voted_fork_slots( fd_gossip_value_t * value,
     477             :                                      uchar const **      payload,
     478          72 :                                      ulong *             payload_sz ) {
     479          72 :   READ_BYTES( value->origin, 32UL, payload, payload_sz );
     480          72 :   READ_WALLCLOCK( value->wallclock, payload, payload_sz );
     481          48 :   uint is_raw_offsets;
     482          48 :   READ_ENUM( is_raw_offsets, 2UL, payload, payload_sz );
     483          48 :   if( FD_LIKELY( is_raw_offsets ) ) {
     484          25 :     CHECK( deser_bitvec_u8_restart_last_voted_fork_slots( payload, payload_sz ) );
     485          25 :   } else {
     486          23 :     ulong slots_len;
     487          23 :     READ_U64( slots_len, payload, payload_sz );
     488         125 :     for( ulong i=0UL; i<slots_len; i++ ) {
     489         102 :       ushort _slot;
     490         102 :       READ_U16_VARINT( _slot, payload, payload_sz );
     491         102 :       (void)_slot;
     492         102 :     }
     493          23 :   }
     494          48 :   SKIP_BYTES( 8UL+32UL+2UL, payload, payload_sz ); /* last voted slot + last voted hash + shred version */
     495          48 :   return 1;
     496          48 : }
     497             : 
     498             : static int
     499             : deser_restart_heaviest_fork( fd_gossip_value_t * value,
     500             :                              uchar const **      payload,
     501          65 :                              ulong *             payload_sz ) {
     502          65 :   READ_BYTES( value->origin, 32UL, payload, payload_sz );
     503          65 :   READ_WALLCLOCK( value->wallclock, payload, payload_sz );
     504          47 :   SKIP_BYTES( 8UL+32UL+8UL+2UL, payload, payload_sz ); /* last slot + last slot hash + observed stake + shred version */
     505          47 :   return 1;
     506          47 : }
     507             : 
     508             : static int
     509             : deser_value( fd_gossip_value_t * value,
     510             :              uchar const **      payload,
     511        1650 :              ulong *             payload_sz ) {
     512        1650 :   READ_BYTES( value->signature, 64UL, payload, payload_sz );
     513        1632 :   READ_ENUM( value->tag, FD_GOSSIP_VALUE_CNT, payload, payload_sz );
     514             : 
     515        1160 :   switch( value->tag ) {
     516          75 :     case FD_GOSSIP_VALUE_LEGACY_CONTACT_INFO:           return 0; /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/gossip/src/legacy_contact_info.rs#L41 */
     517          75 :     case FD_GOSSIP_VALUE_VOTE:                          return deser_vote( value, payload, payload_sz );
     518          94 :     case FD_GOSSIP_VALUE_LOWEST_SLOT:                   return deser_lowest_slot( value, payload, payload_sz );
     519          86 :     case FD_GOSSIP_VALUE_LEGACY_SNAPSHOT_HASHES:        return 0; /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/gossip/src/crds_data.rs#L224 */
     520          83 :     case FD_GOSSIP_VALUE_ACCOUNT_HASHES:                return 0; /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/gossip/src/crds_data.rs#L224 */
     521          79 :     case FD_GOSSIP_VALUE_EPOCH_SLOTS:                   return deser_epoch_slots( value, payload, payload_sz );
     522         105 :     case FD_GOSSIP_VALUE_LEGACY_VERSION:                return 0; /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/gossip/src/crds_data.rs#L431 */
     523          92 :     case FD_GOSSIP_VALUE_VERSION:                       return 0; /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/gossip/src/crds_data.rs#L448 */
     524          91 :     case FD_GOSSIP_VALUE_NODE_INSTANCE:                 return 0; /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/gossip/src/crds_data.rs#L466 */
     525          74 :     case FD_GOSSIP_VALUE_DUPLICATE_SHRED:               return deser_duplicate_shred( value, payload, payload_sz );
     526          86 :     case FD_GOSSIP_VALUE_SNAPSHOT_HASHES:               return deser_snapshot_hashes( value, payload, payload_sz );
     527          84 :     case FD_GOSSIP_VALUE_CONTACT_INFO:                  return deser_contact_info( value, payload, payload_sz );
     528          72 :     case FD_GOSSIP_VALUE_RESTART_LAST_VOTED_FORK_SLOTS: return deser_restart_last_voted_fork_slots( value, payload, payload_sz );
     529          65 :     case FD_GOSSIP_VALUE_RESTART_HEAVIEST_FORK:         return deser_restart_heaviest_fork( value, payload, payload_sz );
     530           0 :     default: FD_LOG_CRIT(( "impossible" ));
     531        1160 :   }
     532        1160 : }
     533             : 
     534             : /* https://github.com/anza-xyz/wincode/blob/wincode%40v0.6.1/wincode/src/schema/external/bv.rs#L89-L147 */
     535             : static int
     536             : deser_bitvec_u64( fd_gossip_bloom_t * bloom,
     537             :                   uchar const **      payload,
     538         562 :                   ulong *             payload_sz ) {
     539         562 :   uchar has_bits;
     540         562 :   READ_OPTION( has_bits, payload, payload_sz );
     541         180 :   if( FD_UNLIKELY( !has_bits ) ) {
     542           2 :     bloom->bits_cap = 0UL;
     543           2 :     READ_U64( bloom->bits_len, payload, payload_sz );
     544           2 :     CHECK( !bloom->bits_len );
     545           1 :     return 1;
     546           2 :   }
     547             : 
     548         178 :   READ_U64( bloom->bits_cap, payload, payload_sz );
     549         178 :   ulong bits_sz;
     550         178 :   CHECK( !__builtin_mul_overflow( bloom->bits_cap, 8UL, &bits_sz ) );
     551          33 :   READ_BYTES( bloom->bits, bits_sz, payload, payload_sz );
     552          11 :   READ_U64( bloom->bits_len, payload, payload_sz );
     553          11 :   CHECK( bloom->bits_len<=bits_sz*8UL );
     554           6 :   return 1;
     555          11 : }
     556             : 
     557             : static int
     558             : deser_pull_request( fd_gossip_message_t * message,
     559             :                     uchar const **        payload,
     560             :                     ulong *               payload_sz,
     561         818 :                     ulong                 original_sz ) {
     562         818 :   READ_U64( message->pull_request->crds_filter->filter->keys_len, payload, payload_sz );
     563       22331 :   for( ulong i=0UL; i<message->pull_request->crds_filter->filter->keys_len; i++ ) {
     564       21769 :     READ_U64( message->pull_request->crds_filter->filter->keys[ i ], payload, payload_sz );
     565       21769 :   }
     566             : 
     567         562 :   CHECK( deser_bitvec_u64( message->pull_request->crds_filter->filter, payload, payload_sz ) );
     568             : 
     569           7 :   READ_U64( message->pull_request->crds_filter->filter->num_bits_set, payload, payload_sz );
     570           7 :   READ_U64( message->pull_request->crds_filter->mask, payload, payload_sz );
     571           7 :   READ_U32( message->pull_request->crds_filter->mask_bits, payload, payload_sz );
     572             : 
     573             :   /* https://github.com/anza-xyz/agave/blob/v4.2.0-beta.0/gossip/src/crds_gossip_pull.rs#L101 */
     574           7 :   CHECK( message->pull_request->crds_filter->mask_bits>=FD_GOSSIP_MIN_PULL_REQUEST_MASK_BITS );
     575             : 
     576           7 :   message->pull_request->contact_info->offset = original_sz-*payload_sz;
     577           7 :   CHECK( deser_value( message->pull_request->contact_info, payload, payload_sz ) );
     578           7 :   message->pull_request->contact_info->length = original_sz-*payload_sz-message->pull_request->contact_info->offset;
     579             :   /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/gossip/src/protocol.rs#L158 */
     580           7 :   CHECK( message->pull_request->contact_info->tag==FD_GOSSIP_VALUE_CONTACT_INFO );
     581           7 :   return 1;
     582           7 : }
     583             : 
     584             : static int
     585             : deser_pull_response( fd_gossip_message_t * message,
     586             :                      uchar const **        payload,
     587             :                      ulong *               payload_sz,
     588         822 :                      ulong                 original_sz ) {
     589         822 :   READ_BYTES( message->pull_response->from, 32UL, payload, payload_sz );
     590         822 :   READ_U64( message->pull_response->values_len, payload, payload_sz );
     591        1014 :   for( ulong i=0UL; i<message->pull_response->values_len; i++ ) {
     592         822 :     message->pull_response->values[ i ].offset = original_sz-*payload_sz;
     593         822 :     CHECK( deser_value( &message->pull_response->values[ i ], payload, payload_sz ) );
     594         192 :     message->pull_response->values[ i ].length = original_sz-*payload_sz-message->pull_response->values[ i ].offset;
     595         192 :   }
     596         192 :   return 1;
     597         822 : }
     598             : 
     599             : static int
     600             : deser_push( fd_gossip_message_t * message,
     601             :             uchar const **        payload,
     602             :             ulong *               payload_sz,
     603         823 :             ulong                 original_sz ) {
     604         823 :   READ_BYTES( message->push->from, 32UL, payload, payload_sz );
     605         823 :   READ_U64( message->push->values_len, payload, payload_sz );
     606        1017 :   for( ulong i=0UL; i<message->push->values_len; i++ ) {
     607         823 :     message->push->values[ i ].offset = original_sz-*payload_sz;
     608         823 :     CHECK( deser_value( &message->push->values[ i ], payload, payload_sz ) );
     609         194 :     message->push->values[ i ].length = original_sz-*payload_sz-message->push->values[ i ].offset;
     610         194 :   }
     611         194 :   return 1;
     612         823 : }
     613             : 
     614             : static int
     615             : deser_prune( fd_gossip_message_t * message,
     616             :              uchar const **        payload,
     617         846 :              ulong *               payload_sz ) {
     618         846 :   READ_BYTES( message->prune->sender, 32UL, payload, payload_sz );
     619         846 :   READ_BYTES( message->prune->pubkey, 32UL, payload, payload_sz );
     620         846 :   CHECK( !memcmp( message->prune->sender, message->prune->pubkey, 32UL ) );
     621         599 :   READ_U64( message->prune->prunes_len, payload, payload_sz );
     622        9163 :   for( ulong i=0UL; i<message->prune->prunes_len; i++ ) {
     623        8564 :     READ_BYTES( message->prune->prunes[ i ], 32UL, payload, payload_sz );
     624        8564 :   }
     625         599 :   READ_BYTES( message->prune->signature, 64UL, payload, payload_sz );
     626         599 :   READ_BYTES( message->prune->destination, 32UL, payload, payload_sz );
     627         599 :   READ_WALLCLOCK( message->prune->wallclock, payload, payload_sz );
     628         599 :   return 1;
     629         599 : }
     630             : 
     631             : static int
     632             : deser_ping( fd_gossip_message_t * message,
     633             :             uchar const **        payload,
     634         850 :             ulong *               payload_sz ) {
     635         850 :   READ_BYTES( message->ping->from, 32UL, payload, payload_sz );
     636         850 :   READ_BYTES( message->ping->token, 32UL, payload, payload_sz );
     637         850 :   READ_BYTES( message->ping->signature, 64UL, payload, payload_sz );
     638         850 :   return 1;
     639         850 : }
     640             : 
     641             : static int
     642             : deser_pong( fd_gossip_message_t * message,
     643             :             uchar const **        payload,
     644         857 :             ulong *               payload_sz ) {
     645         857 :   READ_BYTES( message->pong->from, 32UL, payload, payload_sz );
     646         857 :   READ_BYTES( message->pong->hash, 32UL, payload, payload_sz );
     647         857 :   READ_BYTES( message->pong->signature, 64UL, payload, payload_sz );
     648         857 :   return 1;
     649         857 : }
     650             : 
     651             : int
     652             : fd_gossip_message_deserialize( fd_gossip_message_t * message,
     653             :                                uchar const *         _payload,
     654        5016 :                                ulong                 _payload_sz ) {
     655        5016 :   uchar const ** payload = &_payload;
     656        5016 :   ulong * payload_sz = &_payload_sz;
     657        5016 :   ulong original_sz = _payload_sz;
     658             : 
     659        5016 :   CHECK( _payload_sz<=1232UL );
     660        5016 :   READ_ENUM( message->tag, FD_GOSSIP_MESSAGE_CNT, payload, payload_sz );
     661             : 
     662        5016 :   switch( message->tag ){
     663         818 :     case FD_GOSSIP_MESSAGE_PULL_REQUEST:  CHECK( deser_pull_request( message, payload, payload_sz, original_sz ) ); break;
     664         822 :     case FD_GOSSIP_MESSAGE_PULL_RESPONSE: CHECK( deser_pull_response( message, payload, payload_sz, original_sz ) ); break;
     665         823 :     case FD_GOSSIP_MESSAGE_PUSH:          CHECK( deser_push( message, payload, payload_sz, original_sz ) ); break;
     666         846 :     case FD_GOSSIP_MESSAGE_PRUNE:         CHECK( deser_prune( message, payload, payload_sz ) ); break;
     667         850 :     case FD_GOSSIP_MESSAGE_PING:          CHECK( deser_ping( message, payload, payload_sz ) ); break;
     668         857 :     case FD_GOSSIP_MESSAGE_PONG:          CHECK( deser_pong( message, payload, payload_sz ) ); break;
     669         857 :     default: FD_LOG_CRIT(( "invalid message tag" ));
     670        5016 :   }
     671             : 
     672        2690 :   return !*payload_sz;
     673        5016 : }
     674             : 
     675           0 : #define CHECK1( cond ) do {               \
     676           0 :   if( FD_UNLIKELY( !(cond) ) ) return -1; \
     677           0 : } while( 0 )
     678             : 
     679           0 : #define WRITE_BYTES( src, src_sz, out, out_sz ) do { \
     680           0 :   CHECK1( *out_sz>=src_sz );                         \
     681           0 :   fd_memcpy( *out, src, src_sz );                    \
     682           0 :   (*out) += src_sz;                                  \
     683           0 :   (*out_sz) -= src_sz;                               \
     684           0 : } while( 0 )
     685             : 
     686           0 : #define WRITE_SKIP_BYTES( skip_sz, out, out_sz ) do { \
     687           0 :   CHECK1( *out_sz>=skip_sz );                         \
     688           0 :   (*out) += skip_sz;                                  \
     689           0 :   (*out_sz) -= skip_sz;                               \
     690           0 : } while( 0 )
     691             : 
     692           0 : #define WRITE_U8( val, out, out_sz ) do { \
     693           0 :   CHECK1( *out_sz>=1UL );                 \
     694           0 :   FD_STORE( uchar, *out, val );           \
     695           0 :   (*out) += 1UL;                          \
     696           0 :   (*out_sz) -= 1UL;                       \
     697           0 : } while( 0 )
     698             : 
     699           0 : #define WRITE_U16( val, out, out_sz ) do { \
     700           0 :   CHECK1( *out_sz>=2UL );                  \
     701           0 :   FD_STORE( ushort, *out, val );           \
     702           0 :   (*out) += 2UL;                           \
     703           0 :   (*out_sz) -= 2UL;                        \
     704           0 : } while( 0 )
     705             : 
     706           0 : #define WRITE_U32( val, out, out_sz ) do { \
     707           0 :   CHECK1( *out_sz>=4UL );                  \
     708           0 :   FD_STORE( uint, *out, val );             \
     709           0 :   (*out) += 4UL;                           \
     710           0 :   (*out_sz) -= 4UL;                        \
     711           0 : } while( 0 )
     712             : 
     713           0 : #define WRITE_U64( val, out, out_sz ) do { \
     714           0 :   CHECK1( *out_sz>=8UL );                  \
     715           0 :   FD_STORE( ulong, *out, val );            \
     716           0 :   (*out) += 8UL;                           \
     717           0 :   (*out_sz) -= 8UL;                        \
     718           0 : } while( 0 )
     719             : 
     720           0 : #define WRITE_U16_VARINT( val, out, out_sz ) do {                   \
     721           0 :   ushort _val = (val);                                              \
     722           0 :   if( FD_LIKELY( _val<128U ) ) {                                    \
     723           0 :     CHECK1( *(out_sz)>=1UL );                                       \
     724           0 :     FD_STORE( uchar, *out, (uchar)_val );                           \
     725           0 :     (*out) += 1UL;                                                  \
     726           0 :     (*out_sz) -= 1UL;                                               \
     727           0 :   } else if( FD_LIKELY( _val<16384U ) ) {                           \
     728           0 :     CHECK1( *out_sz>=2UL );                                         \
     729           0 :     FD_STORE( uchar, (*out),   (uchar)((_val&0x7FU)|0x80U) );       \
     730           0 :     FD_STORE( uchar, (*out)+1, (uchar)(_val>>7U) );                 \
     731           0 :     (*out) += 2UL;                                                  \
     732           0 :     (*out_sz) -= 2UL;                                               \
     733           0 :   } else {                                                          \
     734           0 :     CHECK1( *out_sz>=3UL );                                         \
     735           0 :     FD_STORE( uchar, (*out),   (uchar)((_val&0x7FU)|0x80U) );       \
     736           0 :     FD_STORE( uchar, (*out)+1, (uchar)(((_val>>7U)&0x7FU)|0x80U) ); \
     737           0 :     FD_STORE( uchar, (*out)+2, (uchar)(_val>>14U) );                \
     738           0 :     (*out) += 3UL;                                                  \
     739           0 :     (*out_sz) -= 3UL;                                               \
     740           0 :   }                                                                 \
     741           0 : } while( 0 )
     742             : 
     743           0 : #define WRITE_U64_VARINT( val, out, out_sz ) do {           \
     744           0 :   ulong _val = (val);                                       \
     745           0 :   while( _val>=0x80UL ) {                                   \
     746           0 :     CHECK1( *(out_sz)>=1UL );                               \
     747           0 :     FD_STORE( uchar, *out, (uchar)((_val&0x7FUL)|0x80UL) ); \
     748           0 :     (*out) += 1UL;                                          \
     749           0 :     (*out_sz) -= 1UL;                                       \
     750           0 :     _val >>= 7;                                             \
     751           0 :   }                                                         \
     752           0 :   CHECK1( *(out_sz)>=1UL );                                 \
     753           0 :   FD_STORE( uchar, *out, (uchar)_val );                     \
     754           0 :   (*out) += 1UL;                                            \
     755           0 :   (*out_sz) -= 1UL;                                         \
     756           0 : } while( 0 )
     757             : 
     758             : static int
     759             : ser_vote( fd_gossip_value_t const * value,
     760             :           uchar **                  out,
     761           0 :           ulong *                   out_sz ) {
     762           0 :   WRITE_U8( value->vote->index, out, out_sz );
     763           0 :   WRITE_BYTES( value->origin, 32UL, out, out_sz );
     764           0 :   WRITE_BYTES( value->vote->transaction, value->vote->transaction_len, out, out_sz );
     765           0 :   WRITE_U64( value->wallclock, out, out_sz );
     766           0 :   return 1;
     767           0 : }
     768             : 
     769             : static int
     770             : ser_duplicate_shred( fd_gossip_value_t const * value,
     771             :                      uchar **                  out,
     772           0 :                      ulong *                   out_sz ) {
     773           0 :   WRITE_U16( value->duplicate_shred->index, out, out_sz );
     774           0 :   WRITE_BYTES( value->origin, 32UL, out, out_sz );
     775           0 :   WRITE_U64( value->wallclock, out, out_sz );
     776           0 :   WRITE_U64( value->duplicate_shred->slot, out, out_sz );
     777           0 :   WRITE_BYTES( "\0\0\0\0\0", 5UL, out, out_sz ); /* (unused) + shred type (unused) */
     778           0 :   WRITE_U8( value->duplicate_shred->num_chunks, out, out_sz );
     779           0 :   WRITE_U8( value->duplicate_shred->chunk_index, out, out_sz );
     780           0 :   WRITE_U64( value->duplicate_shred->chunk_len, out, out_sz );
     781           0 :   WRITE_BYTES( value->duplicate_shred->chunk, value->duplicate_shred->chunk_len, out, out_sz );
     782           0 :   return 1;
     783           0 : }
     784             : 
     785             : static int
     786             : ser_snapshot_hashes( fd_gossip_value_t const * value,
     787             :                      uchar **                  out,
     788           0 :                      ulong *                   out_sz ) {
     789           0 :   WRITE_BYTES( value->origin, 32UL, out, out_sz );
     790           0 :   WRITE_U64( value->snapshot_hashes->full_slot, out, out_sz );
     791           0 :   WRITE_BYTES( value->snapshot_hashes->full_hash, 32UL, out, out_sz );
     792           0 :   WRITE_U64( value->snapshot_hashes->incremental_len, out, out_sz );
     793           0 :   for( ulong i=0UL; i<value->snapshot_hashes->incremental_len; i++ ) {
     794           0 :     WRITE_U64( value->snapshot_hashes->incremental[ i ].slot, out, out_sz );
     795           0 :     WRITE_BYTES( value->snapshot_hashes->incremental[ i ].hash, 32UL, out, out_sz );
     796           0 :   }
     797           0 :   WRITE_U64( value->wallclock, out, out_sz );
     798           0 :   return 1;
     799           0 : }
     800             : 
     801             : static int
     802             : ser_contact_info( fd_gossip_value_t const * value,
     803             :                   uchar **                  out,
     804           0 :                   ulong *                   out_sz ) {
     805           0 :   WRITE_BYTES( value->origin, 32UL, out, out_sz );
     806           0 :   WRITE_U64_VARINT( value->wallclock, out, out_sz );
     807           0 :   WRITE_U64( value->contact_info->outset, out, out_sz );
     808           0 :   WRITE_U16( value->contact_info->shred_version, out, out_sz );
     809           0 :   WRITE_U16_VARINT( value->contact_info->version.major, out, out_sz );
     810           0 :   WRITE_U16_VARINT( value->contact_info->version.minor, out, out_sz );
     811           0 :   WRITE_U16_VARINT( value->contact_info->version.patch, out, out_sz );
     812           0 :   WRITE_U32( value->contact_info->version.commit, out, out_sz );
     813           0 :   WRITE_U32( value->contact_info->version.feature_set, out, out_sz );
     814           0 :   WRITE_U16_VARINT( value->contact_info->version.client, out, out_sz );
     815             : 
     816           0 :   ulong num_sockets = 0UL;
     817           0 :   ulong num_unique_addrs = 0UL;
     818           0 :   int duplicate[ FD_GOSSIP_CONTACT_INFO_SOCKET_CNT ] = {0};
     819           0 :   ulong address_map[ FD_GOSSIP_CONTACT_INFO_SOCKET_CNT ];
     820           0 :   for( ulong i=0UL; i<FD_GOSSIP_CONTACT_INFO_SOCKET_CNT; i++ ) {
     821           0 :     if( FD_UNLIKELY( !value->contact_info->sockets[ i ].port ) ) continue;
     822           0 :     num_sockets++;
     823             : 
     824           0 :     if( FD_UNLIKELY( duplicate[ i ] ) ) continue;
     825             : 
     826           0 :     address_map[ i ] = num_unique_addrs;
     827           0 :     num_unique_addrs++;
     828             : 
     829           0 :     for( ulong j=i+1UL; j<FD_GOSSIP_CONTACT_INFO_SOCKET_CNT; j++ ) {
     830           0 :       if( FD_UNLIKELY( value->contact_info->sockets[ i ].is_ipv6!=value->contact_info->sockets[ j ].is_ipv6 ) ) continue;
     831           0 :       if( FD_LIKELY( !value->contact_info->sockets[ i ].is_ipv6 ) ) {
     832           0 :         if( FD_LIKELY( value->contact_info->sockets[ i ].ip4!=value->contact_info->sockets[ j ].ip4 ) ) continue;
     833           0 :       } else {
     834           0 :         if( FD_LIKELY( memcmp( value->contact_info->sockets[ i ].ip6, value->contact_info->sockets[ j ].ip6, 16UL ) ) ) continue;
     835           0 :       }
     836             : 
     837           0 :       duplicate[ j ] = 1;
     838           0 :       address_map[ j ] = address_map[ i ];
     839           0 :     }
     840           0 :   }
     841             : 
     842           0 :   WRITE_U16_VARINT( (ushort)num_unique_addrs, out, out_sz );
     843           0 :   for( ulong i=0UL; i<FD_GOSSIP_CONTACT_INFO_SOCKET_CNT; i++ ) {
     844           0 :     if( FD_UNLIKELY( !value->contact_info->sockets[ i ].port ) ) continue;
     845           0 :     if( FD_UNLIKELY( duplicate[ i ] ) ) continue;
     846             : 
     847           0 :     WRITE_U32( value->contact_info->sockets[ i ].is_ipv6, out, out_sz );
     848           0 :     if( FD_LIKELY( !value->contact_info->sockets[ i ].is_ipv6 ) ) WRITE_U32( value->contact_info->sockets[ i ].ip4, out, out_sz );
     849           0 :     else                                                          WRITE_BYTES( value->contact_info->sockets[ i ].ip6, 16UL, out, out_sz );
     850           0 :   }
     851             : 
     852           0 :   WRITE_U16_VARINT( (ushort)num_sockets, out, out_sz );
     853             : 
     854           0 :   int already_written[ FD_GOSSIP_CONTACT_INFO_SOCKET_CNT ] = {0};
     855           0 :   ushort prev_port = 0U;
     856           0 :   for( ulong i=0UL; i<num_sockets; i++ ) {
     857           0 :     ulong lowest_port_index = ULONG_MAX;
     858           0 :     for( ulong j=0UL; j<FD_GOSSIP_CONTACT_INFO_SOCKET_CNT; j++ ) {
     859           0 :       if( FD_UNLIKELY( !value->contact_info->sockets[ j ].port ) ) continue;
     860           0 :       if( FD_UNLIKELY( already_written[ j ] ) ) continue;
     861           0 :       if( FD_UNLIKELY( lowest_port_index==ULONG_MAX || fd_ushort_bswap( value->contact_info->sockets[ j ].port )<fd_ushort_bswap( value->contact_info->sockets[ lowest_port_index ].port ) ) ) lowest_port_index = j;
     862           0 :     }
     863           0 :     if( FD_UNLIKELY( lowest_port_index==ULONG_MAX ) ) break;
     864           0 :     already_written[ lowest_port_index ] = 1;
     865             : 
     866           0 :     WRITE_U8( (uchar)lowest_port_index, out, out_sz );
     867           0 :     WRITE_U8( (uchar)address_map[ lowest_port_index ], out, out_sz );
     868             : 
     869           0 :     ushort port_offset = (ushort)(fd_ushort_bswap( value->contact_info->sockets[ lowest_port_index ].port )-prev_port);
     870           0 :     WRITE_U16_VARINT( port_offset, out, out_sz );
     871           0 :     prev_port = fd_ushort_bswap( value->contact_info->sockets[ lowest_port_index ].port );
     872           0 :   }
     873             : 
     874           0 :   WRITE_U16_VARINT( 0UL, out, out_sz ); /* extensions_len */
     875           0 :   return 1;
     876           0 : }
     877             : 
     878             : long
     879             : fd_gossip_value_serialize( fd_gossip_value_t const * value,
     880             :                            uchar *                   _out,
     881           0 :                            ulong                     _out_sz ) {
     882             : 
     883           0 :   uchar ** out = &_out;
     884           0 :   ulong original_size = _out_sz;
     885           0 :   ulong * out_sz = &_out_sz;
     886             : 
     887           0 :   WRITE_BYTES( value->signature, 64UL, out, out_sz );
     888           0 :   WRITE_U32( value->tag, out, out_sz );
     889             : 
     890           0 :   switch( value->tag ) {
     891           0 :     case FD_GOSSIP_VALUE_VOTE:            if( FD_UNLIKELY( -1==ser_vote( value, out, out_sz ) ) ) return -1; break;
     892           0 :     case FD_GOSSIP_VALUE_DUPLICATE_SHRED: if( FD_UNLIKELY( -1==ser_duplicate_shred( value, out, out_sz ) ) ) return -1; break;
     893           0 :     case FD_GOSSIP_VALUE_SNAPSHOT_HASHES: if( FD_UNLIKELY( -1==ser_snapshot_hashes( value, out, out_sz ) ) ) return -1; break;
     894           0 :     case FD_GOSSIP_VALUE_CONTACT_INFO:    if( FD_UNLIKELY( -1==ser_contact_info( value, out, out_sz ) ) ) return -1; break;
     895             : 
     896             :     // UNUSED VALUES, WE DO NOT SERIALIZE THESE
     897             :     // case FD_GOSSIP_VALUE_LEGACY_CONTACT_INFO:           return ser_legacy_contact_info( value, out, out_sz );
     898             :     // case FD_GOSSIP_VALUE_LOWEST_SLOT:                   return ser_lowest_slot( value, out, out_sz );
     899             :     // case FD_GOSSIP_VALUE_LEGACY_SNAPSHOT_HASHES:        return ser_legacy_snapshot_hashes( value, out, out_sz );
     900             :     // case FD_GOSSIP_VALUE_ACCOUNT_HASHES:                return ser_account_hashes( value, out, out_sz );
     901             :     // case FD_GOSSIP_VALUE_EPOCH_SLOTS:                   return ser_epoch_slots( value, out, out_sz );
     902             :     // case FD_GOSSIP_VALUE_LEGACY_VERSION:                return ser_legacy_version( value, out, out_sz );
     903             :     // case FD_GOSSIP_VALUE_VERSION:                       return ser_version( value, out, out_sz );
     904             :     // case FD_GOSSIP_VALUE_RESTART_LAST_VOTED_FORK_SLOTS: return ser_restart_last_voted_fork_slots( value, out, out_sz );
     905             :     // case FD_GOSSIP_VALUE_RESTART_HEAVIEST_FORK:         return ser_restart_heaviest_fork( value, out, out_sz );
     906           0 :     default: FD_LOG_CRIT(( "impossible" ));
     907           0 :   }
     908             : 
     909           0 :   return (long)(original_size-_out_sz);
     910           0 : }
     911             : 
     912             : long
     913             : fd_gossip_pull_request_init( uchar *       payload,
     914             :                              ulong         payload_sz,
     915             :                              ulong         num_keys,
     916             :                              ulong         num_bits,
     917             :                              ulong         mask,
     918             :                              uint          mask_bits,
     919             :                              uchar const * contact_info_crds,
     920             :                              ulong         contact_info_crds_sz,
     921             :                              uchar **      out_bloom_keys,
     922             :                              uchar **      out_bloom_bits,
     923           0 :                              uchar **      out_bits_set ) {
     924           0 :   uchar ** out = &payload;
     925           0 :   ulong original_size = payload_sz;
     926           0 :   ulong * out_sz = &payload_sz;
     927             : 
     928           0 :   WRITE_U32( FD_GOSSIP_MESSAGE_PULL_REQUEST, out, out_sz );
     929           0 :   WRITE_U64( num_keys, out, out_sz );
     930           0 :   *out_bloom_keys = payload+(payload_sz-*out_sz);
     931           0 :   WRITE_SKIP_BYTES( num_keys*8UL, out, out_sz );
     932             : 
     933           0 :   if( FD_LIKELY( !!num_bits ) ) {
     934             :     /* Bloom bits is a bitvec<u64>, so we need to be careful about converting bloom bits count to vector lengths */
     935           0 :     ulong bloom_vec_len = (num_bits+63UL)/64UL;
     936           0 :     WRITE_U8( 1, out, out_sz ); /* has_bits */
     937           0 :     WRITE_U64( bloom_vec_len, out, out_sz );
     938           0 :     *out_bloom_bits = payload+(payload_sz-*out_sz);
     939           0 :     WRITE_SKIP_BYTES( bloom_vec_len*8UL, out, out_sz );
     940           0 :   } else {
     941           0 :     WRITE_U8( 0, out, out_sz ); /* has_bits */
     942           0 :     *out_bloom_bits = NULL;
     943           0 :   }
     944           0 :   WRITE_U64( num_bits, out, out_sz );
     945           0 :   *out_bits_set = payload+(payload_sz-*out_sz);
     946           0 :   WRITE_SKIP_BYTES( 8UL, out, out_sz );
     947           0 :   WRITE_U64( mask, out, out_sz );
     948           0 :   WRITE_U32( mask_bits, out, out_sz );
     949           0 :   WRITE_BYTES( contact_info_crds, contact_info_crds_sz, out, out_sz );
     950             : 
     951           0 :   return (long)(original_size-*out_sz);
     952           0 : }

Generated by: LCOV version 1.14