LCOV - code coverage report
Current view: top level - flamenco/gossip - fd_gossip_private.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 4 54 7.4 %
Date: 2025-10-27 04:40:00 Functions: 0 60 0.0 %

          Line data    Source code
       1             : 
       2             : #ifndef HEADER_fd_src_flamenco_gossip_fd_gossip_private_h
       3             : #define HEADER_fd_src_flamenco_gossip_fd_gossip_private_h
       4             : 
       5             : #include "fd_gossip_types.h"
       6             : #include "../../util/fd_util.h"
       7             : #include "../../disco/fd_disco_base.h"
       8             : 
       9             : #include <stddef.h> // offsetof
      10             : 
      11             : /* Constants used in deriving size bounds
      12             :    - 1232b (MTU)
      13             :    - 1188b = 1232b-4b(discriminant)-32b(pubkey)-8(crds len) max CRDS sz
      14             :      largest CRDS value seen so far is duplicate shreds at 1187b*/
      15             : 
      16           3 : #define FD_GOSSIP_CRDS_MAX_SZ (1188UL)
      17             : 
      18             : /* Deriving maximum number of CRDS values a message can hold:
      19             :   - Each CRDS value contains a 64b signature and a 4b discriminant.
      20             :     So, each CRDS value is at least 68b.
      21             :   - Smallest CRDS data is technically slot hashes with zero hashes,
      22             :     which is 32b (pubkey) + 8b (wallclock) + 8b (vector len), bringing
      23             :     total to 68b+32b+8b+8b=116b
      24             :   - However, we take a more conservative approach and assume just the
      25             :     signature and discriminant.
      26             :   - So, maximum number of CRDS values is 1188/(68) ~= 18 */
      27           0 : #define FD_GOSSIP_MSG_MAX_CRDS (18UL)
      28             : 
      29           0 : #define FD_GOSSIP_MESSAGE_PULL_REQUEST  (0)
      30           0 : #define FD_GOSSIP_MESSAGE_PULL_RESPONSE (1)
      31           0 : #define FD_GOSSIP_MESSAGE_PUSH          (2)
      32           0 : #define FD_GOSSIP_MESSAGE_PRUNE         (3)
      33           0 : #define FD_GOSSIP_MESSAGE_PING          (4)
      34           0 : #define FD_GOSSIP_MESSAGE_PONG          (5)
      35             : #define FD_GOSSIP_MESSAGE_LAST          (FD_GOSSIP_MESSAGE_PONG)
      36             : 
      37           0 : #define FD_GOSSIP_VALUE_LEGACY_CONTACT_INFO           ( 0)
      38           0 : #define FD_GOSSIP_VALUE_VOTE                          ( 1)
      39           0 : #define FD_GOSSIP_VALUE_LOWEST_SLOT                   ( 2) // SOME FIELDS DEPRECATED
      40           0 : #define FD_GOSSIP_VALUE_LEGACY_SNAPSHOT_HASHES        ( 3) // DEPRECATED
      41           0 : #define FD_GOSSIP_VALUE_ACCOUNT_HASHES                ( 4) // DEPRECATED
      42           0 : #define FD_GOSSIP_VALUE_EPOCH_SLOTS                   ( 5)
      43           0 : #define FD_GOSSIP_VALUE_LEGACY_VERSION                ( 6)
      44           0 : #define FD_GOSSIP_VALUE_VERSION                       ( 7)
      45           0 : #define FD_GOSSIP_VALUE_NODE_INSTANCE                 ( 8)
      46           0 : #define FD_GOSSIP_VALUE_DUPLICATE_SHRED               ( 9)
      47           0 : #define FD_GOSSIP_VALUE_INC_SNAPSHOT_HASHES           (10)
      48         450 : #define FD_GOSSIP_VALUE_CONTACT_INFO                  (11)
      49           0 : #define FD_GOSSIP_VALUE_RESTART_LAST_VOTED_FORK_SLOTS (12)
      50           0 : #define FD_GOSSIP_VALUE_RESTART_HEAVIEST_FORK         (13)
      51             : #define FD_GOSSIP_VALUE_LAST                          (FD_GOSSIP_VALUE_RESTART_HEAVIEST_FORK)
      52             : 
      53             : /* Gossip messages encode wallclock in millis*, while we
      54             :    parse them into nanoseconds for internal use.
      55             : 
      56             :    * exceptions:
      57             :      - Contact Info outset (AKA instance creation wallclock) is encoded
      58             :        in micros */
      59           3 : #define FD_NANOSEC_TO_MILLI(_ts_) ((long)(_ts_/1000000))
      60           0 : #define FD_MILLI_TO_NANOSEC(_ts_) ((long)(_ts_*1000000))
      61           3 : #define FD_NANOSEC_TO_MICRO(_ts_) ((long)(_ts_/1000))
      62           0 : #define FD_MICRO_TO_NANOSEC(_ts_) ((long)(_ts_*1000))
      63             : 
      64             : /* Bound max inc entries by
      65             :     1188b (max CRDS encoded buffer size )
      66             :   - 64b (signature)
      67             :   - 4b  (tag)
      68             :   - 32b (pubkey)
      69             :   - 40b (full pair)
      70             :   - 8b  (inc len)
      71             :   - 8b  (wallclock)
      72             :   = 1032b
      73             : 
      74             :   1032b/40 ~= 25 */
      75             : FD_STATIC_ASSERT( FD_GOSSIP_SNAPSHOT_HASHES_MAX_INCREMENTAL==25UL,
      76             :                  "FD_GOSSIP_SNAPSHOT_HASHES_MAX_INCREMENTAL must be 25" );
      77             : 
      78             : 
      79           0 : #define FD_GOSSIP_UPDATE_SZ_CONTACT_INFO        (offsetof(fd_gossip_update_message_t, contact_info)        + sizeof(ulong) + sizeof(fd_contact_info_t))
      80           0 : #define FD_GOSSIP_UPDATE_SZ_CONTACT_INFO_REMOVE (offsetof(fd_gossip_update_message_t, contact_info_remove) + sizeof(ulong))
      81           0 : #define FD_GOSSIP_UPDATE_SZ_LOWEST_SLOT         (offsetof(fd_gossip_update_message_t, lowest_slot)         + sizeof(ulong))
      82           0 : #define FD_GOSSIP_UPDATE_SZ_VOTE                (offsetof(fd_gossip_update_message_t, vote)                + sizeof(fd_gossip_vote_t))
      83           0 : #define FD_GOSSIP_UPDATE_SZ_DUPLICATE_SHRED     (offsetof(fd_gossip_update_message_t, duplicate_shred)     + sizeof(fd_gossip_duplicate_shred_t))
      84           0 : #define FD_GOSSIP_UPDATE_SZ_SNAPSHOT_HASHES     (offsetof(fd_gossip_update_message_t, snapshot_hashes)     + sizeof(fd_gossip_snapshot_hashes_t))
      85             : 
      86             : struct fd_gossip_view_ipaddr {
      87             :   uchar   is_ip6;
      88             :   union {
      89             :     uint   ip4;
      90             :     ushort ip6_off;
      91             :   };
      92             : };
      93             : 
      94             : typedef struct fd_gossip_view_ipaddr fd_gossip_view_ipaddr_t;
      95             : 
      96             : struct fd_gossip_view_socket {
      97             :   uchar   key;
      98             :   uchar   index;
      99             :   ushort  offset; /* NOTE: this is a varint in encoded form */
     100             : };
     101             : 
     102             : typedef struct fd_gossip_view_socket fd_gossip_view_socket_t;
     103             : 
     104             : /* To get the minimum possible wire size of a Version message, we use
     105             :    version 0.0.0 client 0 (anything less than 128 works):
     106             : 
     107             :      1b (major)
     108             :    + 1b (minor)
     109             :    + 1b (patch)
     110             :    + 4b (commit)
     111             :    + 4b (feature set)
     112             :    + 1b (client)
     113             :    = 12b */
     114             : struct fd_gossip_view_version {
     115             :   ushort major;
     116             :   ushort minor;
     117             :   ushort patch;
     118             :   uint   commit;      /* First 4 bytes of the commit hash */
     119             :   uint   feature_set; /* Feature set encoded as a bitmask */
     120             :   ushort client;
     121             : };
     122             : 
     123             : typedef struct fd_gossip_view_version fd_gossip_view_version_t;
     124             : 
     125             : /* Contact info size bound calculations:
     126             : 
     127             :    The minimal valid contact info would hold 0 addrs, 0 sockets,
     128             :    and an empty extensions array. This ends up taking
     129             : 
     130             :      32b (pubkey)
     131             :    + 8b  (wallclock)
     132             :    + 8b  (outset)
     133             :    + 2b  (shred version)
     134             :    + 12b (minimum version)
     135             :    + 1b  (addrs_len)
     136             :    + 1b  (sockets_len)
     137             :    + 1b  (ext_len)
     138             :    = 65b
     139             : 
     140             :   This leaves us with 1188b - 65b = 1123b to hold addrs, sockets or
     141             :   extensions. Extension is just a byte array, so we can ignore sizing it
     142             :   and instead offset it in the payload.
     143             : 
     144             :   Before analyzing size bounds for addrs and sockets, we establish the
     145             :   minimum size socket entry:
     146             :      1b (key)
     147             :    + 1b (index)
     148             :    + 1b (offset, compact-u16)
     149             :    = 3b
     150             : 
     151             :   According to Agave's ContactInfo verifier (linked below), every IP
     152             :   address must be unique, and must be referenced by at least one socket.
     153             :   This means that the number of addrs must be at most the number of
     154             :   sockets. So to find the maximum n (addr, socket) pairs we can fit in
     155             :   1123b:
     156             :     1123b / (8b (addr) + 3b (socket)) ~= 102 pairs.
     157             : 
     158             :   This bounds the number of addrs to 102. We cannot apply this bound to
     159             :   sockets, because the socket entries can reference the same addr
     160             :   multiple times, so we can have just 1 addr and use the remaining space
     161             :   to hold sockets.
     162             : 
     163             :   Agave's verifier enforces a unique socket tag (key) across all
     164             :   sockets, and since the key is 1b, this bounds us to 256 sockets.
     165             : 
     166             :   https://github.com/anza-xyz/agave/blob/540d5bc56cd44e3cc61b179bd52e9a782a2c99e4/gossip/src/contact_info.rs#L599-L643 */
     167             : 
     168             : #define FD_GOSSIP_CONTACT_INFO_MAX_ADDRESSES (102UL)
     169             : #define FD_GOSSIP_CONTACT_INFO_MAX_SOCKETS   (256UL)
     170             : 
     171             : struct fd_gossip_view_contact_info {
     172             :   fd_contact_info_t contact_info[1];
     173             :   ulong             ip6_cnt;
     174             :   ulong             unrecognized_socket_tag_cnt;
     175             : 
     176             :   ushort            ext_len;
     177             :   ushort            ext_off;
     178             : };
     179             : 
     180             : typedef struct fd_gossip_view_contact_info fd_gossip_view_contact_info_t;
     181             : 
     182             : struct fd_gossip_view_node_instance {
     183             :   ulong token;
     184             : };
     185             : 
     186             : typedef struct fd_gossip_view_node_instance fd_gossip_view_node_instance_t;
     187             : 
     188             : #define FD_GOSSIP_VOTE_IDX_MAX (32U)
     189             : struct fd_gossip_view_vote {
     190             :   uchar  index;
     191             :   ulong  txn_sz;
     192             :   ushort txn_off;
     193             : };
     194             : 
     195             : typedef struct fd_gossip_view_vote fd_gossip_view_vote_t;
     196             : 
     197             : #define FD_GOSSIP_EPOCH_SLOTS_IDX_MAX (255U)
     198             : struct fd_gossip_view_epoch_slots {
     199             :   uchar  index;
     200             : };
     201             : 
     202             : typedef struct fd_gossip_view_epoch_slots fd_gossip_view_epoch_slots_t;
     203             : 
     204             : #define FD_GOSSIP_DUPLICATE_SHRED_IDX_MAX (512U)
     205             : struct fd_gossip_view_duplicate_shred {
     206             :   ushort index;
     207             :   ulong  slot;
     208             :   uchar  num_chunks;
     209             :   uchar  chunk_index;
     210             :   ulong  chunk_len;
     211             :   ushort chunk_off;
     212             : };
     213             : 
     214             : typedef struct fd_gossip_view_duplicate_shred fd_gossip_view_duplicate_shred_t;
     215             : 
     216             : typedef struct fd_gossip_view_snapshot_hash_pair fd_gossip_view_snapshot_hash_pair_t;
     217             : struct fd_gossip_view_snapshot_hashes {
     218             :   ushort full_off; /* Offset to the full snapshot hashes (slot, hash) pair */
     219             :   ulong  inc_len;
     220             :   ushort inc_off;  /* Offset to start of incremental snapshot hashes pair */
     221             : };
     222             : 
     223             : typedef struct fd_gossip_view_snapshot_hashes fd_gossip_view_snapshot_hashes_t;
     224             : 
     225             :  /* Offsets are within full message payload, not the subset where the encoded
     226             :     CRDS value lies. */
     227             : struct fd_gossip_view_crds_value {
     228             :   union {
     229             :     ushort value_off; /* Start of CRDS value data in payload */
     230             :     ushort signature_off;
     231             :   };
     232             :   ushort pubkey_off;
     233             :   long   wallclock_nanos;
     234             :   ushort length; /* Length of the value in bytes (incl. signature) */
     235             : 
     236             :   uchar tag;
     237             :   union{
     238             :     fd_gossip_view_contact_info_t    ci_view[ 1 ];
     239             :     fd_gossip_view_node_instance_t   node_instance[ 1 ];
     240             :     fd_gossip_view_vote_t            vote[ 1 ];
     241             :     fd_gossip_view_epoch_slots_t     epoch_slots[ 1 ];
     242             :     fd_gossip_view_duplicate_shred_t duplicate_shred[ 1 ];
     243             :     ulong                            lowest_slot;
     244             :     fd_gossip_view_snapshot_hashes_t snapshot_hashes[ 1 ];
     245             :   };
     246             : };
     247             : 
     248             : typedef struct fd_gossip_view_crds_value fd_gossip_view_crds_value_t;
     249             : 
     250             : struct fd_gossip_view_crds_container {
     251             :   ushort from_off;
     252             :   ushort crds_values_len;
     253             : 
     254             :   fd_gossip_view_crds_value_t crds_values[ FD_GOSSIP_MSG_MAX_CRDS ];
     255             : };
     256             : 
     257             : typedef struct fd_gossip_view_crds_container fd_gossip_view_crds_container_t;
     258             : typedef struct fd_gossip_view_crds_container fd_gossip_view_pull_response_t;
     259             : typedef struct fd_gossip_view_crds_container fd_gossip_view_push_t;
     260             : struct fd_gossip_view_pull_request {
     261             :   ulong bloom_keys_len;
     262             :   ulong bloom_keys_offset;
     263             : 
     264             :   ulong bloom_len;
     265             :   ulong bloom_bits_offset;
     266             :   ulong bloom_bits_cnt;
     267             : 
     268             :   ulong bloom_num_bits_set;
     269             :   ulong mask;
     270             :   uint  mask_bits;
     271             : 
     272             :   fd_gossip_view_crds_value_t pr_ci[ 1 ];
     273             : };
     274             : 
     275             : typedef struct fd_gossip_view_pull_request fd_gossip_view_pull_request_t;
     276             : 
     277             : struct fd_gossip_view_prune {
     278             :   ushort pubkey_off;
     279             :   ulong  origins_len;
     280             :   ushort origins_off;
     281             :   ushort destination_off;
     282             :   ulong  wallclock;
     283             :   ushort signature_off;
     284             : 
     285             :   long   wallclock_nanos;
     286             : };
     287             : 
     288             : typedef struct fd_gossip_view_prune fd_gossip_view_prune_t;
     289             : 
     290             : /* Ping/Pong can be casted on to the payload bytes
     291             :    directly */
     292             : struct __attribute__((__packed__)) fd_gossip_view_ping {
     293             :   uchar pubkey[ 32UL ];
     294             :   uchar ping_token[ 32UL ];
     295             :   uchar signature[ 64UL ];
     296             : };
     297             : 
     298             : typedef struct fd_gossip_view_ping fd_gossip_view_ping_t;
     299             : 
     300             : struct __attribute__((__packed__)) fd_gossip_view_pong {
     301             :   uchar pubkey[ 32UL ];
     302             :   uchar ping_hash[ 32UL ];
     303             :   uchar signature[ 64UL ];
     304             : };
     305             : 
     306             : typedef struct fd_gossip_view_pong fd_gossip_view_pong_t;
     307             : 
     308             : struct fd_gossip_view {
     309             :   uchar tag; // uint in rust bincode
     310             :   union {
     311             :     fd_gossip_view_pull_request_t  pull_request[ 1 ];
     312             :     fd_gossip_view_pull_response_t pull_response[ 1 ];
     313             :     fd_gossip_view_push_t          push[ 1 ];
     314             :     fd_gossip_view_prune_t         prune[ 1 ];
     315             :     ushort                         ping_pong_off;
     316             :   };
     317             : };
     318             : 
     319             : typedef struct fd_gossip_view fd_gossip_view_t;
     320             : 
     321             : static inline fd_ip4_port_t
     322             : fd_contact_info_get_socket( fd_contact_info_t const * ci,
     323           0 :                             uchar                     tag ) {
     324           0 :   if( FD_UNLIKELY( tag>=FD_CONTACT_INFO_SOCKET_CNT ) ) {
     325           0 :     FD_LOG_ERR(( "Invalid socket tag %u", tag ));
     326           0 :   }
     327           0 :   return ci->sockets[ tag ];
     328           0 : }
     329             : 
     330             : static inline fd_ip4_port_t
     331           0 : fd_contact_info_gossip_socket( fd_contact_info_t const * ci ) {
     332           0 :   return fd_contact_info_get_socket( ci, FD_CONTACT_INFO_SOCKET_GOSSIP );
     333           0 : }
     334             : 
     335             : ulong
     336             : fd_gossip_msg_parse( fd_gossip_view_t * view,
     337             :                      uchar const *      payload,
     338             :                      ulong              payload_sz );
     339             : 
     340             : FD_FN_CONST static inline ulong
     341             : fd_gossip_pull_request_max_filter_bits( ulong num_keys,
     342             :                                         ulong contact_info_crds_sz,
     343           0 :                                         ulong payload_sz ) {
     344           0 :   return 8UL*( payload_sz
     345           0 :              - 4UL          /* discriminant */
     346           0 :              - 8UL          /* keys len */
     347           0 :              - 8UL*num_keys /* keys */
     348           0 :              - 1UL          /* has_bits */
     349           0 :              - 8UL          /* bloom vec len */
     350           0 :              - 8UL          /* bloom bits count */
     351           0 :              - 8UL          /* bloom num bits set */
     352           0 :              - 8UL          /* mask */
     353           0 :              - 4UL          /* mask bits */
     354           0 :              - contact_info_crds_sz ); /* contact info CRDS val */
     355           0 : }
     356             : 
     357             : int
     358             : fd_gossip_pull_request_init( uchar *       payload,
     359             :                              ulong         payload_sz,
     360             :                              ulong         num_keys,
     361             :                              ulong         num_bits,
     362             :                              ulong         mask,
     363             :                              uint          mask_bits,
     364             :                              uchar const * contact_info_crds,
     365             :                              ulong         contact_info_crds_sz,
     366             :                              ulong **      out_bloom_keys,
     367             :                              ulong **      out_bloom_bits,
     368             :                              ulong **      out_bits_set,
     369             :                              ulong *       out_payload_sz );
     370             : 
     371             : int
     372             : fd_gossip_contact_info_encode( fd_contact_info_t const * contact_info,
     373             :                                uchar *                   out_buf,
     374             :                                ulong                     out_buf_cap,
     375             :                                ulong *                   opt_encoded_sz );
     376             : 
     377             : int
     378             : fd_gossip_crds_vote_encode( uchar *       out_buf,
     379             :                             ulong         out_buf_sz,
     380             :                             uchar const * txn,
     381             :                             ulong         txn_sz,
     382             :                             uchar const * identity_pubkey,
     383             :                             long          now,
     384             :                             ulong *       opt_encoded_sz );
     385             : #endif /* HEADER_fd_src_flamenco_gossip_fd_gossip_private_h */

Generated by: LCOV version 1.14