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-13 04:42:14 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             : struct fd_gossip_view_vote {
     189             :   uchar  index;
     190             :   ulong  txn_sz;
     191             :   ushort txn_off;
     192             : };
     193             : 
     194             : typedef struct fd_gossip_view_vote fd_gossip_view_vote_t;
     195             : 
     196             : struct fd_gossip_view_epoch_slots {
     197             :   uchar  index;
     198             : };
     199             : 
     200             : typedef struct fd_gossip_view_epoch_slots fd_gossip_view_epoch_slots_t;
     201             : 
     202             : struct fd_gossip_view_duplicate_shred {
     203             :   ushort index;
     204             :   ulong  slot;
     205             :   uchar  num_chunks;
     206             :   uchar  chunk_index;
     207             :   ulong  chunk_len;
     208             :   ushort chunk_off;
     209             : };
     210             : 
     211             : typedef struct fd_gossip_view_duplicate_shred fd_gossip_view_duplicate_shred_t;
     212             : 
     213             : typedef struct fd_gossip_view_snapshot_hash_pair fd_gossip_view_snapshot_hash_pair_t;
     214             : struct fd_gossip_view_snapshot_hashes {
     215             :   ushort full_off; /* Offset to the full snapshot hashes (slot, hash) pair */
     216             :   ulong  inc_len;
     217             :   ushort inc_off;  /* Offset to start of incremental snapshot hashes pair */
     218             : };
     219             : 
     220             : typedef struct fd_gossip_view_snapshot_hashes fd_gossip_view_snapshot_hashes_t;
     221             : 
     222             :  /* Offsets are within full message payload, not the subset where the encoded
     223             :     CRDS value lies. */
     224             : struct fd_gossip_view_crds_value {
     225             :   union {
     226             :     ushort value_off; /* Start of CRDS value data in payload */
     227             :     ushort signature_off;
     228             :   };
     229             :   ushort pubkey_off;
     230             :   long   wallclock_nanos;
     231             :   ushort length; /* Length of the value in bytes (incl. signature) */
     232             : 
     233             :   uchar tag;
     234             :   union{
     235             :     fd_gossip_view_contact_info_t    ci_view[ 1 ];
     236             :     fd_gossip_view_node_instance_t   node_instance[ 1 ];
     237             :     fd_gossip_view_vote_t            vote[ 1 ];
     238             :     fd_gossip_view_epoch_slots_t     epoch_slots[ 1 ];
     239             :     fd_gossip_view_duplicate_shred_t duplicate_shred[ 1 ];
     240             :     ulong                            lowest_slot;
     241             :     fd_gossip_view_snapshot_hashes_t snapshot_hashes[ 1 ];
     242             :   };
     243             : };
     244             : 
     245             : typedef struct fd_gossip_view_crds_value fd_gossip_view_crds_value_t;
     246             : 
     247             : struct fd_gossip_view_crds_container {
     248             :   ushort from_off;
     249             :   ushort crds_values_len;
     250             : 
     251             :   fd_gossip_view_crds_value_t crds_values[ FD_GOSSIP_MSG_MAX_CRDS ];
     252             : };
     253             : 
     254             : typedef struct fd_gossip_view_crds_container fd_gossip_view_crds_container_t;
     255             : typedef struct fd_gossip_view_crds_container fd_gossip_view_pull_response_t;
     256             : typedef struct fd_gossip_view_crds_container fd_gossip_view_push_t;
     257             : struct fd_gossip_view_pull_request {
     258             :   ulong bloom_keys_len;
     259             :   ulong bloom_keys_offset;
     260             : 
     261             :   ulong bloom_len;
     262             :   ulong bloom_bits_offset;
     263             :   ulong bloom_bits_cnt;
     264             : 
     265             :   ulong bloom_num_bits_set;
     266             :   ulong mask;
     267             :   uint  mask_bits;
     268             : 
     269             :   fd_gossip_view_crds_value_t pr_ci[ 1 ];
     270             : };
     271             : 
     272             : typedef struct fd_gossip_view_pull_request fd_gossip_view_pull_request_t;
     273             : 
     274             : struct fd_gossip_view_prune {
     275             :   ushort pubkey_off;
     276             :   ulong  origins_len;
     277             :   ushort origins_off;
     278             :   ushort destination_off;
     279             :   ulong  wallclock;
     280             :   ushort signature_off;
     281             : 
     282             :   long   wallclock_nanos;
     283             : };
     284             : 
     285             : typedef struct fd_gossip_view_prune fd_gossip_view_prune_t;
     286             : 
     287             : /* Ping/Pong can be casted on to the payload bytes
     288             :    directly */
     289             : struct __attribute__((__packed__)) fd_gossip_view_ping {
     290             :   uchar pubkey[ 32UL ];
     291             :   uchar ping_token[ 32UL ];
     292             :   uchar signature[ 64UL ];
     293             : };
     294             : 
     295             : typedef struct fd_gossip_view_ping fd_gossip_view_ping_t;
     296             : 
     297             : struct __attribute__((__packed__)) fd_gossip_view_pong {
     298             :   uchar pubkey[ 32UL ];
     299             :   uchar ping_hash[ 32UL ];
     300             :   uchar signature[ 64UL ];
     301             : };
     302             : 
     303             : typedef struct fd_gossip_view_pong fd_gossip_view_pong_t;
     304             : 
     305             : struct fd_gossip_view {
     306             :   uchar tag; // uint in rust bincode
     307             :   union {
     308             :     fd_gossip_view_pull_request_t  pull_request[ 1 ];
     309             :     fd_gossip_view_pull_response_t pull_response[ 1 ];
     310             :     fd_gossip_view_push_t          push[ 1 ];
     311             :     fd_gossip_view_prune_t         prune[ 1 ];
     312             :     ushort                         ping_pong_off;
     313             :   };
     314             : };
     315             : 
     316             : typedef struct fd_gossip_view fd_gossip_view_t;
     317             : 
     318             : static inline fd_ip4_port_t
     319             : fd_contact_info_get_socket( fd_contact_info_t const * ci,
     320           0 :                             uchar                     tag ) {
     321           0 :   if( FD_UNLIKELY( tag>=FD_CONTACT_INFO_SOCKET_CNT ) ) {
     322           0 :     FD_LOG_ERR(( "Invalid socket tag %u", tag ));
     323           0 :   }
     324           0 :   return ci->sockets[ tag ];
     325           0 : }
     326             : 
     327             : static inline fd_ip4_port_t
     328           0 : fd_contact_info_gossip_socket( fd_contact_info_t const * ci ) {
     329           0 :   return fd_contact_info_get_socket( ci, FD_CONTACT_INFO_SOCKET_GOSSIP );
     330           0 : }
     331             : 
     332             : ulong
     333             : fd_gossip_msg_parse( fd_gossip_view_t * view,
     334             :                      uchar const *      payload,
     335             :                      ulong              payload_sz );
     336             : 
     337             : FD_FN_CONST static inline ulong
     338             : fd_gossip_pull_request_max_filter_bits( ulong num_keys,
     339             :                                         ulong contact_info_crds_sz,
     340           0 :                                         ulong payload_sz ) {
     341           0 :   return 8UL*( payload_sz
     342           0 :              - 4UL          /* discriminant */
     343           0 :              - 8UL          /* keys len */
     344           0 :              - 8UL*num_keys /* keys */
     345           0 :              - 1UL          /* has_bits */
     346           0 :              - 8UL          /* bloom vec len */
     347           0 :              - 8UL          /* bloom bits count */
     348           0 :              - 8UL          /* bloom num bits set */
     349           0 :              - 8UL          /* mask */
     350           0 :              - 4UL          /* mask bits */
     351           0 :              - contact_info_crds_sz ); /* contact info CRDS val */
     352           0 : }
     353             : 
     354             : int
     355             : fd_gossip_pull_request_init( uchar *       payload,
     356             :                              ulong         payload_sz,
     357             :                              ulong         num_keys,
     358             :                              ulong         num_bits,
     359             :                              ulong         mask,
     360             :                              uint          mask_bits,
     361             :                              uchar const * contact_info_crds,
     362             :                              ulong         contact_info_crds_sz,
     363             :                              ulong **      out_bloom_keys,
     364             :                              ulong **      out_bloom_bits,
     365             :                              ulong **      out_bits_set,
     366             :                              ulong *       out_payload_sz );
     367             : 
     368             : int
     369             : fd_gossip_contact_info_encode( fd_contact_info_t const * contact_info,
     370             :                                uchar *                   out_buf,
     371             :                                ulong                     out_buf_cap,
     372             :                                ulong *                   opt_encoded_sz );
     373             : 
     374             : int
     375             : fd_gossip_crds_vote_encode( uchar *       out_buf,
     376             :                             ulong         out_buf_sz,
     377             :                             uchar const * txn,
     378             :                             ulong         txn_sz,
     379             :                             uchar const * identity_pubkey,
     380             :                             long          now,
     381             :                             ulong *       opt_encoded_sz );
     382             : #endif /* HEADER_fd_src_flamenco_gossip_fd_gossip_private_h */

Generated by: LCOV version 1.14