LCOV - code coverage report
Current view: top level - flamenco/gossip - fd_gossip_message.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 9 60 15.0 %
Date: 2026-08-20 04:31:44 Functions: 0 58 0.0 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_flamenco_gossip_fd_gossip_message_h
       2             : #define HEADER_fd_src_flamenco_gossip_fd_gossip_message_h
       3             : 
       4             : #include "../../util/fd_util_base.h"
       5             : #include "../../util/cstr/fd_cstr.h"
       6             : #include "fd_gossip_value.h"
       7             : 
       8             : #include <stddef.h>
       9             : 
      10             : /* The maximum number of contact infos that may be present at any one
      11             :    time.  If new contact infos are added, a removal will be issued first
      12             :    to make space.  This is a hard limit, and the consumer of the contact
      13             :    info messages can assume it is always respected.
      14             : 
      15             :    The contact info messages are designed to be consumed in an
      16             :    incremental way.  In particular, CONTACT_INFO and CONTACT_INFO_REMOVE
      17             :    messages are both sent with an idx field, which is the index of the
      18             :    contact info in an imaginary array of contact infos.  Updates will
      19             :    always have the same idx for the same pubkey, and removes will
      20             :    likewise have the same idx for the pubkey being removed.  A consumer
      21             :    of contact info updates can therefore simply maintain a local array
      22             :    of contact infos, and update it with the idx field.  */
      23             : 
      24          57 : #define FD_CONTACT_INFO_TABLE_SIZE (32768UL)
      25             : 
      26             : /* Tightest bound for a single CrdsValue given network constraints.
      27             : 
      28             :      IPv6 minimum MTU             = 1280
      29             :      IPv6 header                  =   40
      30             :      UDP header                   =    8
      31             :      PACKET_DATA_SIZE             = 1232   (= 1280 - 40 - 8)
      32             : 
      33             :      Maximum CrdsValue size inside PushMessage/PullResponse:
      34             :        PACKET_DATA_SIZE - tag(4) - from(32) - values_len(8)  = 1188  */
      35             : 
      36           0 : #define FD_GOSSIP_VALUE_MAX_SZ (1188UL)
      37             : 
      38             : /* Tightest bound for values[] in a Push / PullResponse given network
      39             :    constraints.
      40             : 
      41             :      IPv6 minimum MTU             = 1280
      42             :      IPv6 header                  =   40
      43             :      UDP header                   =    8
      44             :      PACKET_DATA_SIZE             = 1232   (= 1280 - 40 - 8)
      45             : 
      46             :      Minimum bytes consumed before values loop:
      47             :        Protocol tag(4) + from(32) + values_len(8)             =  44
      48             : 
      49             :      Remaining: 1232 - 44 = 1188
      50             :      Each CrdsValue: signature(64) + CrdsData tag(4) = 68 bytes minimum
      51             :      Max values = floor(1188/68) = 17  */
      52             : 
      53           0 : #define FD_GOSSIP_MESSAGE_MAX_CRDS (17UL)
      54             : 
      55           0 : #define FD_GOSSIP_FAILED_NO_CONTACT_INFO (1)
      56           0 : #define FD_GOSSIP_FAILED_WALLCLOCK       (2)
      57             : 
      58             : /* FD_GOSSIP_STAKED_THRESHOLD is the minimum stake (in lamports) at
      59             :    which a node is considered an active participant (e.g. it does not
      60             :    need to maintain a connection by responding to pings), whereas nodes
      61             :    below this threshold are treated as unstaked.  This is 100 SOL. */
      62           3 : #define FD_GOSSIP_STAKED_THRESHOLD (100UL*1000000000UL)
      63             : 
      64          21 : #define FD_GOSSIP_UPDATE_SZ_CONTACT_INFO        (offsetof(fd_gossip_update_message_t, contact_info)        + sizeof((fd_gossip_update_message_t *)0)->contact_info)
      65           6 : #define FD_GOSSIP_UPDATE_SZ_CONTACT_INFO_REMOVE (offsetof(fd_gossip_update_message_t, contact_info_remove) + sizeof((fd_gossip_update_message_t *)0)->contact_info_remove)
      66           0 : #define FD_GOSSIP_UPDATE_SZ_VOTE                (offsetof(fd_gossip_update_message_t, vote)                + sizeof((fd_gossip_update_message_t *)0)->vote)
      67           0 : #define FD_GOSSIP_UPDATE_SZ_DUPLICATE_SHRED     (offsetof(fd_gossip_update_message_t, duplicate_shred)     + sizeof((fd_gossip_update_message_t *)0)->duplicate_shred)
      68           0 : #define FD_GOSSIP_UPDATE_SZ_SNAPSHOT_HASHES     (offsetof(fd_gossip_update_message_t, snapshot_hashes)     + sizeof((fd_gossip_update_message_t *)0)->snapshot_hashes)
      69             : 
      70             : /* Gossip messages encode wallclock in millis*, while we
      71             :    parse them into nanoseconds for internal use.
      72             : 
      73             :    * exceptions:
      74             :      - Contact Info outset (AKA instance creation wallclock) is encoded
      75             :        in micros */
      76           0 : #define FD_NANOSEC_TO_MILLI(_ts_) ((long)(_ts_/1000000))
      77           0 : #define FD_MILLI_TO_NANOSEC(_ts_) ((long)(_ts_*1000000))
      78           0 : #define FD_NANOSEC_TO_MICRO(_ts_) ((long)(_ts_/1000))
      79           0 : #define FD_MICRO_TO_NANOSEC(_ts_) ((long)(_ts_*1000))
      80             : 
      81         117 : #define FD_GOSSIP_UPDATE_TAG_CONTACT_INFO        (0)
      82          51 : #define FD_GOSSIP_UPDATE_TAG_CONTACT_INFO_REMOVE (1)
      83           0 : #define FD_GOSSIP_UPDATE_TAG_VOTE                (2)
      84           0 : #define FD_GOSSIP_UPDATE_TAG_DUPLICATE_SHRED     (3)
      85           0 : #define FD_GOSSIP_UPDATE_TAG_SNAPSHOT_HASHES     (4)
      86           0 : #define FD_GOSSIP_UPDATE_TAG_WFS_DONE            (5)
      87           0 : #define FD_GOSSIP_UPDATE_TAG_PEER_SATURATED      (6)
      88             : 
      89           0 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP            ( 0)
      90           0 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_SERVE_REPAIR_QUIC ( 1)
      91          36 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_RPC               ( 2)
      92           0 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_RPC_PUBSUB        ( 3)
      93           0 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_SERVE_REPAIR      ( 4)
      94           0 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_TPU               ( 5)
      95           0 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_FORWARDS      ( 6)
      96           0 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_FORWARDS_QUIC ( 7)
      97           0 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_QUIC          ( 8)
      98           0 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_VOTE          ( 9)
      99           0 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_TVU               (10)
     100           0 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_TVU_QUIC          (11)
     101           0 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_VOTE_QUIC     (12)
     102           0 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_ALPENGLOW         (13)
     103           0 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_CNT               (14)
     104             : 
     105           0 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_SOLANA_LABS   (0)
     106           0 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_JITO_LABS     (1)
     107           0 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_FRANKENDANCER (2)
     108           0 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_AGAVE         (3)
     109           0 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_AGAVE_PALADIN (4)
     110           0 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_FIREDANCER    (5)
     111           0 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_AGAVE_BAM     (6)
     112           0 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_SIG           (7)
     113             : 
     114          21 : #define FD_GOSSIP_MESSAGE_PULL_REQUEST  (0)
     115           0 : #define FD_GOSSIP_MESSAGE_PULL_RESPONSE (1)
     116          21 : #define FD_GOSSIP_MESSAGE_PUSH          (2)
     117           0 : #define FD_GOSSIP_MESSAGE_PRUNE         (3)
     118           0 : #define FD_GOSSIP_MESSAGE_PING          (4)
     119           0 : #define FD_GOSSIP_MESSAGE_PONG          (5)
     120             : #define FD_GOSSIP_MESSAGE_CNT           (6)
     121             : 
     122             : struct fd_gossip_vote {
     123             :   uchar index;
     124             :   ulong transaction_len;
     125             :   uchar transaction[ 1232UL ];
     126             : };
     127             : 
     128             : typedef struct fd_gossip_vote fd_gossip_vote_t;
     129             : 
     130             : struct fd_gossip_node_instance {
     131             :   ulong timestamp;
     132             :   ulong token;
     133             : };
     134             : 
     135             : typedef struct fd_gossip_node_instance fd_gossip_node_instance_t;
     136             : 
     137             : /* Tightest bound for chunk[] given network constraints.
     138             : 
     139             :      IPv6 minimum MTU             = 1280
     140             :      IPv6 header                  =   40
     141             :      UDP header                   =    8
     142             :      PACKET_DATA_SIZE             = 1232   (= 1280 - 40 - 8)
     143             : 
     144             :      Maximum CrdsValue size inside PushMessage/PullResponse:
     145             :        PACKET_DATA_SIZE - tag(4) - from(32) - values_len(8) = 1188
     146             : 
     147             :      Minimum bytes consumed before chunk data:
     148             :        signature(64) + CrdsData tag(4) + index(2) + origin(32) +
     149             :        wallclock(8) + slot(8) + unused(4) + shred_type(1) +
     150             :        num_chunks(1) + chunk_index(1) + chunk_len(8)         = 133
     151             : 
     152             :      Remaining: 1188 - 133 = 1055  */
     153             : 
     154             : struct fd_gossip_duplicate_shred {
     155             :   ushort index;
     156             :   ulong  slot;
     157             :   uchar  num_chunks;
     158             :   uchar  chunk_index;
     159             :   ulong  chunk_len;
     160             :   uchar  chunk[ 1055UL ];
     161             : };
     162             : 
     163             : typedef struct fd_gossip_duplicate_shred fd_gossip_duplicate_shred_t;
     164             : 
     165             : /* Tightest bound for incremental[] given network constraints.
     166             : 
     167             :      IPv6 minimum MTU             = 1280
     168             :      IPv6 header                  =   40
     169             :      UDP header                   =    8
     170             :      PACKET_DATA_SIZE             = 1232   (= 1280 - 40 - 8)
     171             : 
     172             :      Maximum CrdsValue size inside PushMessage/PullResponse:
     173             :        PACKET_DATA_SIZE - tag(4) - from(32) - values_len(8) = 1188
     174             : 
     175             :      Bytes consumed before incremental loop:
     176             :        signature(64) + CrdsData tag(4) + origin(32) +
     177             :        full_slot(8) + full_hash(32) + inc_len(8)             = 148
     178             : 
     179             :      Remaining: 1188 - 148 = 1040
     180             :      Each entry: slot(8) + hash(32) = 40 bytes
     181             :      Max entries = floor(1040/40) = 26  */
     182             : 
     183             : struct fd_gossip_snapshot_hashes {
     184             :   ulong full_slot;
     185             :   uchar full_hash[ 32UL ];
     186             : 
     187             :   ulong incremental_len;
     188             :   struct {
     189             :     ulong slot;
     190             :     uchar hash[ 32UL ];
     191             :   } incremental[ 26UL ];
     192             : };
     193             : 
     194             : typedef struct fd_gossip_snapshot_hashes fd_gossip_snapshot_hashes_t;
     195             : 
     196             : struct fd_gossip_socket {
     197             :   ushort port;
     198             :   uint   is_ipv6;
     199             :   union {
     200             :     uint ip4;
     201             :     uchar ip6[ 16UL ];
     202             :   };
     203             : };
     204             : 
     205             : typedef struct fd_gossip_socket fd_gossip_socket_t;
     206             : 
     207             : struct fd_gossip_contact_info {
     208             :   ulong  outset;
     209             :   ushort shred_version;
     210             : 
     211             :   struct {
     212             :     ushort major;
     213             :     ushort minor;
     214             :     ushort patch;
     215             : 
     216             :     uint   commit;
     217             :     uint   feature_set;
     218             : 
     219             :     ushort client;
     220             :   } version;
     221             : 
     222             :   fd_gossip_socket_t sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_CNT ];
     223             : };
     224             : 
     225             : typedef struct fd_gossip_contact_info fd_gossip_contact_info_t;
     226             : 
     227             : struct fd_gossip_epoch_slots {
     228             :   uchar index;
     229             : };
     230             : 
     231             : typedef struct fd_gossip_epoch_slots fd_gossip_epoch_slots_t;
     232             : 
     233             : struct fd_gossip_lowest_slot {
     234             :   ulong lowest;
     235             : };
     236             : 
     237             : typedef struct fd_gossip_lowest_slot fd_gossip_lowest_slot_t;
     238             : 
     239             : struct fd_gossip_value {
     240             :   uint tag;
     241             : 
     242             :   uchar signature[ 64UL ];
     243             :   uchar origin[ 32UL ];
     244             :   ulong wallclock;
     245             : 
     246             :   ulong offset;
     247             :   ulong length;
     248             : 
     249             :   union {
     250             :     // DEPRECATED OR UNUSED
     251             :     // fd_gossip_legacy_contact_info_t           legacy_contact_info[ 1 ];
     252             :     // fd_gossip_legacy_snapshot_hashes_t        legacy_snapshot_hashes[ 1 ];
     253             :     // fd_gossip_account_hashes_t                account_hashes[ 1 ];
     254             :     // fd_gossip_legacy_version_t                legacy_version[ 1 ];
     255             :     // fd_gossip_version_t                       version[ 1 ];
     256             :     // fd_gossip_restart_last_voted_fork_slots_t restart_last_voted_fork_slots[ 1 ];
     257             :     // fd_gossip_restart_heaviest_fork_t         restart_heaviest_fork[ 1 ];
     258             : 
     259             :     fd_gossip_lowest_slot_t     lowest_slot[ 1 ];
     260             :     fd_gossip_vote_t            vote[ 1 ];
     261             :     fd_gossip_node_instance_t   node_instance[ 1 ];
     262             :     fd_gossip_duplicate_shred_t duplicate_shred[ 1 ];
     263             :     fd_gossip_snapshot_hashes_t snapshot_hashes[ 1 ];
     264             :     fd_gossip_contact_info_t    contact_info[ 1 ];
     265             :     fd_gossip_epoch_slots_t     epoch_slots[ 1 ];
     266             :   };
     267             : };
     268             : 
     269             : typedef struct fd_gossip_value fd_gossip_value_t;
     270             : 
     271             : /* Tightest bounds for Bloom keys[]/bits[] given network constraints.
     272             : 
     273             :      IPv6 minimum MTU             = 1280
     274             :      IPv6 header                  =   40
     275             :      UDP header                   =    8
     276             :      PACKET_DATA_SIZE             = 1232   (= 1280 - 40 - 8)
     277             : 
     278             :      PullRequest is the only message containing a Bloom filter.
     279             : 
     280             :      Bytes consumed before keys loop:
     281             :        Protocol tag(4) + keys_len(8)                         =  12
     282             : 
     283             :      Remaining: 1232 - 12 = 1220
     284             :      Each key: 8 bytes
     285             :      Max keys = floor(1220/8) = 152
     286             : 
     287             :      Bytes consumed before bits data:
     288             :        Protocol tag(4) + keys_len(8) + has_bits(1) +
     289             :        bits_cap(8)                                           =  21
     290             : 
     291             :      Remaining: 1232 - 21 = 1211
     292             :      Each u64: 8 bytes
     293             :      Max bits = floor(1211/8) = 151  */
     294             : 
     295             : struct fd_gossip_bloom {
     296             :   ulong keys_len;
     297             :   ulong keys[ 152UL ];
     298             :   ulong bits_cap;
     299             :   ulong bits_len;
     300             :   ulong bits[ 151UL ];
     301             :   ulong num_bits_set;
     302             : };
     303             : 
     304             : typedef struct fd_gossip_bloom fd_gossip_bloom_t;
     305             : 
     306             : struct fd_gossip_crds_filter {
     307             :   fd_gossip_bloom_t filter[ 1 ];
     308             :   ulong mask;
     309             :   uint mask_bits;
     310             : };
     311             : 
     312             : typedef struct fd_gossip_crds_filter fd_gossip_crds_filter_t;
     313             : 
     314             : struct fd_gossip_pull_request {
     315             :   fd_gossip_crds_filter_t crds_filter[ 1 ];
     316             : 
     317             :   fd_gossip_value_t contact_info[ 1 ];
     318             : };
     319             : 
     320             : typedef struct fd_gossip_pull_request fd_gossip_pull_request_t;
     321             : 
     322             : struct fd_gossip_pull_response {
     323             :   uchar from[ 32UL ];
     324             :   ulong values_len;
     325             :   fd_gossip_value_t values[ FD_GOSSIP_MESSAGE_MAX_CRDS ];
     326             : };
     327             : 
     328             : typedef struct fd_gossip_pull_response fd_gossip_pull_response_t;
     329             : 
     330             : struct fd_gossip_push {
     331             :   uchar from[ 32UL ];
     332             :   ulong values_len;
     333             :   fd_gossip_value_t values[ FD_GOSSIP_MESSAGE_MAX_CRDS ];
     334             : };
     335             : 
     336             : typedef struct fd_gossip_push fd_gossip_push_t;
     337             : 
     338             : /* Tightest bound for prunes[] given network constraints.
     339             : 
     340             :      IPv6 minimum MTU             = 1280
     341             :      IPv6 header                  =   40
     342             :      UDP header                   =    8
     343             :      PACKET_DATA_SIZE             = 1232   (= 1280 - 40 - 8)
     344             : 
     345             :      Bytes consumed before prunes loop:
     346             :        Protocol tag(4) + sender(32) + pubkey(32) +
     347             :        prunes_len(8)                                         =  76
     348             : 
     349             :      Remaining: 1232 - 76 = 1156
     350             :      Each prune: 32 bytes
     351             :      Max prunes = floor(1156/32) = 36  */
     352             : 
     353             : struct fd_gossip_prune {
     354             :   uchar sender[ 32UL ];
     355             :   uchar pubkey[ 32UL ];
     356             :   ulong prunes_len;
     357             :   uchar prunes[ 36UL ][ 32UL ];
     358             :   uchar signature[ 64UL ];
     359             :   uchar destination[ 32UL ];
     360             :   ulong wallclock;
     361             : };
     362             : 
     363             : typedef struct fd_gossip_prune fd_gossip_prune_t;
     364             : 
     365             : struct fd_gossip_ping {
     366             :   uchar from[ 32UL ];
     367             :   uchar token[ 32UL ];
     368             :   uchar signature[ 64UL ];
     369             : };
     370             : 
     371             : typedef struct fd_gossip_ping fd_gossip_ping_t;
     372             : 
     373             : struct fd_gossip_pong {
     374             :   uchar from[ 32UL ];
     375             :   uchar hash[ 32UL ];
     376             :   uchar signature[ 64UL ];
     377             : };
     378             : 
     379             : typedef struct fd_gossip_pong fd_gossip_pong_t;
     380             : 
     381             : struct fd_gossip_message {
     382             :   uint tag;
     383             : 
     384             :   union {
     385             :     fd_gossip_pull_request_t  pull_request[ 1 ];
     386             :     fd_gossip_pull_response_t pull_response[ 1 ];
     387             :     fd_gossip_push_t          push[ 1 ];
     388             :     fd_gossip_prune_t         prune[ 1 ];
     389             :     fd_gossip_ping_t          ping[ 1 ];
     390             :     fd_gossip_pong_t          pong[ 1 ];
     391             :   };
     392             : };
     393             : 
     394             : typedef struct fd_gossip_message fd_gossip_message_t;
     395             : 
     396             : int
     397             : fd_gossip_message_deserialize( fd_gossip_message_t * message,
     398             :                                uchar const *         payload,
     399             :                                ulong                 payload_sz );
     400             : 
     401             : long
     402             : fd_gossip_value_serialize( fd_gossip_value_t const * value,
     403             :                            uchar *                   out,
     404             :                            ulong                     out_sz );
     405             : 
     406             : struct fd_gossip_update_message {
     407             :   int tag;
     408             : 
     409             :   uchar origin[ 32UL ];
     410             :   ulong wallclock;
     411             : 
     412             :   union {
     413             :     struct {
     414             :       ulong idx;
     415             :       fd_gossip_contact_info_t value[ 1 ];
     416             :     } contact_info[ 1 ];
     417             : 
     418             :     struct {
     419             :       ulong idx;
     420             :     } contact_info_remove[ 1 ];
     421             : 
     422             :     struct {
     423             :       long first_seen_nanos;
     424             :       fd_gossip_socket_t socket[ 1 ];
     425             :       fd_gossip_vote_t value[ 1 ];
     426             :     } vote[ 1 ];
     427             : 
     428             :     fd_gossip_duplicate_shred_t duplicate_shred[ 1 ];
     429             :     fd_gossip_snapshot_hashes_t snapshot_hashes[ 1 ];
     430             :   };
     431             : };
     432             : 
     433             : typedef struct fd_gossip_update_message fd_gossip_update_message_t;
     434             : 
     435             : long
     436             : fd_gossip_pull_request_init( uchar *       payload,
     437             :                              ulong         payload_sz,
     438             :                              ulong         num_keys,
     439             :                              ulong         num_bits,
     440             :                              ulong         mask,
     441             :                              uint          mask_bits,
     442             :                              uchar const * contact_info_crds,
     443             :                              ulong         contact_info_crds_sz,
     444             :                              ulong **      out_bloom_keys,
     445             :                              ulong **      out_bloom_bits,
     446             :                              ulong **      out_bits_set );
     447             : 
     448             : /* fd_gossip_version_cstr converts gossip version fields to a null
     449             :    terminated c-string.  Returns 1 on success and 0 on failure (e.g.
     450             :    small out_sz)
     451             : 
     452             :    The 16-bit minor field has a special encoding to support semver
     453             :    prerelease notation.
     454             :     - High 2 bits: prerelease channel (0=stable, 1=rc, 2=beta, 3=alpha)
     455             :     - Low 14 bits: actual minor version number
     456             : 
     457             :     Patch field semantics:
     458             :         - If prerelease_bits==0 (stable), `patch` is rendered as the
     459             :           normal semver patch component:
     460             :           `<major>.<minor_actual>.<patch>`.
     461             :         - If prerelease_bits!=0 (prerelease), the semver patch component
     462             :           is forced to 0 and `patch` is interpreted as the prerelease
     463             :           sequence number, rendered as:
     464             :           `<major>.<minor_actual>.0-<channel>.<patch>`.
     465             : 
     466             :     Note that due to Frankendancer's existing unique versioning hack,
     467             :     future Frankendancer releases will all be stable (i.e.
     468             :     prerelease_bits==0) and based on stable Agave versions.  This is a
     469             :     permissible compromise given Frankendancer is approaching EOL. */
     470             : static inline int
     471             : fd_gossip_version_cstr( ushort major,
     472             :                  ushort minor,
     473             :                  ushort patch,
     474             :                  char * out,
     475           0 :                  ulong  out_sz ) {
     476           0 :   ushort prerelease_bits = (minor >> 14U) & 0x3U;
     477           0 :   ushort minor_actual = minor & 0x3FFFU;
     478             : 
     479           0 :   if( FD_UNLIKELY( prerelease_bits ) ) {
     480           0 :     const char * names[] = { "", "rc", "beta", "alpha" };
     481           0 :     return fd_cstr_printf_check( out, out_sz, NULL, "%hu.%hu.0-%s.%hu", major, minor_actual, names[ prerelease_bits ], patch );
     482           0 :   } else {
     483             :     return fd_cstr_printf_check( out, out_sz, NULL, "%hu.%hu.%hu", major, minor_actual, patch );
     484           0 :   }
     485           0 : }
     486             : 
     487             : #endif /* HEADER_fd_src_flamenco_gossip_fd_gossip_message_h */

Generated by: LCOV version 1.14