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 1772544 : #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 0 : #define FD_GOSSIP_FAILED_DUPLICATE (3)
58 :
59 : /* FD_GOSSIP_STAKED_THRESHOLD is the minimum stake (in lamports) at
60 : which a node is considered an active participant (e.g. it does not
61 : need to maintain a connection by responding to pings), whereas nodes
62 : below this threshold are treated as unstaked. This is 100 SOL. */
63 21 : #define FD_GOSSIP_STAKED_THRESHOLD (100UL*1000000000UL)
64 :
65 315 : #define FD_GOSSIP_UPDATE_SZ_CONTACT_INFO (offsetof(fd_gossip_update_message_t, contact_info) + sizeof((fd_gossip_update_message_t *)0)->contact_info)
66 60 : #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)
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 63675 : #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 1620 : #define FD_GOSSIP_UPDATE_TAG_CONTACT_INFO (0)
82 567 : #define FD_GOSSIP_UPDATE_TAG_CONTACT_INFO_REMOVE (1)
83 0 : #define FD_GOSSIP_UPDATE_TAG_DUPLICATE_SHRED (3)
84 0 : #define FD_GOSSIP_UPDATE_TAG_SNAPSHOT_HASHES (4)
85 0 : #define FD_GOSSIP_UPDATE_TAG_WFS_DONE (5)
86 0 : #define FD_GOSSIP_UPDATE_TAG_PEER_SATURATED (6)
87 :
88 144 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ( 0)
89 27 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_SERVE_REPAIR_QUIC ( 1)
90 1215 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_RPC ( 2)
91 27 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_RPC_PUBSUB ( 3)
92 27 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_SERVE_REPAIR ( 4)
93 27 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_TPU ( 5)
94 27 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_FORWARDS ( 6)
95 27 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_FORWARDS_QUIC ( 7)
96 27 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_QUIC ( 8)
97 27 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_VOTE ( 9)
98 123 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_TVU (10)
99 27 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_TVU_QUIC (11)
100 27 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_VOTE_QUIC (12)
101 51 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_ALPENGLOW (13)
102 504 : #define FD_GOSSIP_CONTACT_INFO_SOCKET_CNT (14)
103 :
104 0 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_SOLANA_LABS (0)
105 0 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_JITO_LABS (1)
106 0 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_FRANKENDANCER (2)
107 0 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_AGAVE (3)
108 0 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_AGAVE_PALADIN (4)
109 0 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_FIREDANCER (5)
110 0 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_AGAVE_BAM (6)
111 0 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_SIG (7)
112 0 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_RAKURAI (8)
113 0 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_HARMONIC_FIREDANCER (9)
114 0 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_HARMONIC_AGAVE (10)
115 0 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_HARMONIC_FRANKENDANCER (11)
116 0 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_FIRE_BAM (12)
117 0 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_RAIKU (13)
118 :
119 21 : #define FD_GOSSIP_MESSAGE_PULL_REQUEST (0)
120 0 : #define FD_GOSSIP_MESSAGE_PULL_RESPONSE (1)
121 36 : #define FD_GOSSIP_MESSAGE_PUSH (2)
122 0 : #define FD_GOSSIP_MESSAGE_PRUNE (3)
123 0 : #define FD_GOSSIP_MESSAGE_PING (4)
124 0 : #define FD_GOSSIP_MESSAGE_PONG (5)
125 : #define FD_GOSSIP_MESSAGE_CNT (6)
126 :
127 : struct fd_gossip_vote {
128 : uchar index;
129 : ulong transaction_len;
130 : uchar transaction[ 1232UL ];
131 : };
132 :
133 : typedef struct fd_gossip_vote fd_gossip_vote_t;
134 :
135 : struct fd_gossip_node_instance {
136 : ulong timestamp;
137 : ulong token;
138 : };
139 :
140 : typedef struct fd_gossip_node_instance fd_gossip_node_instance_t;
141 :
142 : /* Tightest bound for chunk[] given network constraints.
143 :
144 : IPv6 minimum MTU = 1280
145 : IPv6 header = 40
146 : UDP header = 8
147 : PACKET_DATA_SIZE = 1232 (= 1280 - 40 - 8)
148 :
149 : Maximum CrdsValue size inside PushMessage/PullResponse:
150 : PACKET_DATA_SIZE - tag(4) - from(32) - values_len(8) = 1188
151 :
152 : Minimum bytes consumed before chunk data:
153 : signature(64) + CrdsData tag(4) + index(2) + origin(32) +
154 : wallclock(8) + slot(8) + unused(4) + shred_type(1) +
155 : num_chunks(1) + chunk_index(1) + chunk_len(8) = 133
156 :
157 : Remaining: 1188 - 133 = 1055 */
158 :
159 : struct fd_gossip_duplicate_shred {
160 : ushort index;
161 : ulong slot;
162 : uchar num_chunks;
163 : uchar chunk_index;
164 : ulong chunk_len;
165 : uchar chunk[ 1055UL ];
166 : };
167 :
168 : typedef struct fd_gossip_duplicate_shred fd_gossip_duplicate_shred_t;
169 :
170 : /* Tightest bound for incremental[] given network constraints.
171 :
172 : IPv6 minimum MTU = 1280
173 : IPv6 header = 40
174 : UDP header = 8
175 : PACKET_DATA_SIZE = 1232 (= 1280 - 40 - 8)
176 :
177 : Maximum CrdsValue size inside PushMessage/PullResponse:
178 : PACKET_DATA_SIZE - tag(4) - from(32) - values_len(8) = 1188
179 :
180 : Bytes consumed before incremental loop:
181 : signature(64) + CrdsData tag(4) + origin(32) +
182 : full_slot(8) + full_hash(32) + inc_len(8) = 148
183 :
184 : Remaining: 1188 - 148 = 1040
185 : Each entry: slot(8) + hash(32) = 40 bytes
186 : Max entries = floor(1040/40) = 26 */
187 :
188 : struct fd_gossip_snapshot_hashes {
189 : ulong full_slot;
190 : uchar full_hash[ 32UL ];
191 :
192 : ulong incremental_len;
193 : struct {
194 : ulong slot;
195 : uchar hash[ 32UL ];
196 : } incremental[ 26UL ];
197 : };
198 :
199 : typedef struct fd_gossip_snapshot_hashes fd_gossip_snapshot_hashes_t;
200 :
201 : struct fd_gossip_socket {
202 : ushort port;
203 : uint is_ipv6;
204 : union {
205 : uint ip4;
206 : uchar ip6[ 16UL ];
207 : };
208 : };
209 :
210 : typedef struct fd_gossip_socket fd_gossip_socket_t;
211 :
212 : struct fd_gossip_contact_info {
213 : ulong outset;
214 : ushort shred_version;
215 :
216 : struct {
217 : ushort major;
218 : ushort minor;
219 : ushort patch;
220 :
221 : uint commit;
222 : uint feature_set;
223 :
224 : ushort client;
225 : } version;
226 :
227 : fd_gossip_socket_t sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_CNT ];
228 : };
229 :
230 : typedef struct fd_gossip_contact_info fd_gossip_contact_info_t;
231 :
232 : FD_FN_PURE static inline int
233 : fd_gossip_contact_info_eq( fd_gossip_contact_info_t const * ci1,
234 15 : fd_gossip_contact_info_t const * ci2 ) {
235 15 : if( ci1->shred_version !=ci2->shred_version ||
236 15 : ci1->outset !=ci2->outset ||
237 15 : ci1->version.client !=ci2->version.client ||
238 15 : ci1->version.major !=ci2->version.major ||
239 15 : ci1->version.minor !=ci2->version.minor ||
240 15 : ci1->version.patch !=ci2->version.patch ||
241 15 : ci1->version.commit !=ci2->version.commit ||
242 15 : ci1->version.feature_set!=ci2->version.feature_set ) return 0;
243 :
244 99 : for( ulong j=0UL; j<FD_GOSSIP_CONTACT_INFO_SOCKET_CNT; j++ ) {
245 93 : if( ci1->sockets[ j ].is_ipv6!=ci2->sockets[ j ].is_ipv6 ) return 0;
246 93 : if( ci1->sockets[ j ].is_ipv6 ) {
247 0 : if( memcmp( ci1->sockets[ j ].ip6, ci2->sockets[ j ].ip6, 16UL ) ) return 0;
248 93 : } else {
249 93 : if( ci1->sockets[ j ].ip4!=ci2->sockets[ j ].ip4 ) return 0;
250 93 : }
251 84 : if( ci1->sockets[ j ].port!=ci2->sockets[ j ].port ) return 0;
252 84 : }
253 6 : return 1;
254 15 : }
255 :
256 : struct fd_gossip_epoch_slots {
257 : uchar index;
258 : };
259 :
260 : typedef struct fd_gossip_epoch_slots fd_gossip_epoch_slots_t;
261 :
262 : struct fd_gossip_lowest_slot {
263 : ulong lowest;
264 : };
265 :
266 : typedef struct fd_gossip_lowest_slot fd_gossip_lowest_slot_t;
267 :
268 : struct fd_gossip_value {
269 : uint tag;
270 :
271 : uchar signature[ 64UL ];
272 : uchar origin[ 32UL ];
273 : ulong wallclock;
274 :
275 : ulong offset;
276 : ulong length;
277 :
278 : union {
279 : // DEPRECATED OR UNUSED
280 : // fd_gossip_legacy_contact_info_t legacy_contact_info[ 1 ];
281 : // fd_gossip_legacy_snapshot_hashes_t legacy_snapshot_hashes[ 1 ];
282 : // fd_gossip_account_hashes_t account_hashes[ 1 ];
283 : // fd_gossip_legacy_version_t legacy_version[ 1 ];
284 : // fd_gossip_version_t version[ 1 ];
285 : // fd_gossip_restart_last_voted_fork_slots_t restart_last_voted_fork_slots[ 1 ];
286 : // fd_gossip_restart_heaviest_fork_t restart_heaviest_fork[ 1 ];
287 :
288 : fd_gossip_lowest_slot_t lowest_slot[ 1 ];
289 : fd_gossip_vote_t vote[ 1 ];
290 : fd_gossip_node_instance_t node_instance[ 1 ];
291 : fd_gossip_duplicate_shred_t duplicate_shred[ 1 ];
292 : fd_gossip_snapshot_hashes_t snapshot_hashes[ 1 ];
293 : fd_gossip_contact_info_t contact_info[ 1 ];
294 : fd_gossip_epoch_slots_t epoch_slots[ 1 ];
295 : };
296 : };
297 :
298 : typedef struct fd_gossip_value fd_gossip_value_t;
299 :
300 : /* Tightest bounds for Bloom keys[]/bits[] given network constraints.
301 :
302 : IPv6 minimum MTU = 1280
303 : IPv6 header = 40
304 : UDP header = 8
305 : PACKET_DATA_SIZE = 1232 (= 1280 - 40 - 8)
306 :
307 : PullRequest is the only message containing a Bloom filter.
308 :
309 : Bytes consumed before keys loop:
310 : Protocol tag(4) + keys_len(8) = 12
311 :
312 : Remaining: 1232 - 12 = 1220
313 : Each key: 8 bytes
314 : Max keys = floor(1220/8) = 152
315 :
316 : Bytes consumed before bits data:
317 : Protocol tag(4) + keys_len(8) + has_bits(1) +
318 : bits_cap(8) = 21
319 :
320 : Remaining: 1232 - 21 = 1211
321 : Each u64: 8 bytes
322 : Max bits = floor(1211/8) = 151 */
323 :
324 : struct fd_gossip_bloom {
325 : ulong keys_len;
326 : ulong keys[ 152UL ];
327 : ulong bits_cap;
328 : ulong bits_len;
329 : ulong bits[ 151UL ];
330 : ulong num_bits_set;
331 : };
332 :
333 : typedef struct fd_gossip_bloom fd_gossip_bloom_t;
334 :
335 : struct fd_gossip_crds_filter {
336 : fd_gossip_bloom_t filter[ 1 ];
337 : ulong mask;
338 : uint mask_bits;
339 : };
340 :
341 : typedef struct fd_gossip_crds_filter fd_gossip_crds_filter_t;
342 :
343 : struct fd_gossip_pull_request {
344 : fd_gossip_crds_filter_t crds_filter[ 1 ];
345 :
346 : fd_gossip_value_t contact_info[ 1 ];
347 : };
348 :
349 : typedef struct fd_gossip_pull_request fd_gossip_pull_request_t;
350 :
351 : struct fd_gossip_pull_response {
352 : uchar from[ 32UL ];
353 : ulong values_len;
354 : fd_gossip_value_t values[ FD_GOSSIP_MESSAGE_MAX_CRDS ];
355 : };
356 :
357 : typedef struct fd_gossip_pull_response fd_gossip_pull_response_t;
358 :
359 : struct fd_gossip_push {
360 : uchar from[ 32UL ];
361 : ulong values_len;
362 : fd_gossip_value_t values[ FD_GOSSIP_MESSAGE_MAX_CRDS ];
363 : };
364 :
365 : typedef struct fd_gossip_push fd_gossip_push_t;
366 :
367 : /* Tightest bound for prunes[] given network constraints.
368 :
369 : IPv6 minimum MTU = 1280
370 : IPv6 header = 40
371 : UDP header = 8
372 : PACKET_DATA_SIZE = 1232 (= 1280 - 40 - 8)
373 :
374 : Bytes consumed before prunes loop:
375 : Protocol tag(4) + sender(32) + pubkey(32) +
376 : prunes_len(8) = 76
377 :
378 : Remaining: 1232 - 76 = 1156
379 : Each prune: 32 bytes
380 : Max prunes = floor(1156/32) = 36 */
381 :
382 : struct fd_gossip_prune {
383 : uchar sender[ 32UL ];
384 : uchar pubkey[ 32UL ];
385 : ulong prunes_len;
386 : uchar prunes[ 36UL ][ 32UL ];
387 : uchar signature[ 64UL ];
388 : uchar destination[ 32UL ];
389 : ulong wallclock;
390 : };
391 :
392 : typedef struct fd_gossip_prune fd_gossip_prune_t;
393 :
394 : struct fd_gossip_ping {
395 : uchar from[ 32UL ];
396 : uchar token[ 32UL ];
397 : uchar signature[ 64UL ];
398 : };
399 :
400 : typedef struct fd_gossip_ping fd_gossip_ping_t;
401 :
402 : struct fd_gossip_pong {
403 : uchar from[ 32UL ];
404 : uchar hash[ 32UL ];
405 : uchar signature[ 64UL ];
406 : };
407 :
408 : typedef struct fd_gossip_pong fd_gossip_pong_t;
409 :
410 : struct fd_gossip_message {
411 : uint tag;
412 :
413 : union {
414 : fd_gossip_pull_request_t pull_request[ 1 ];
415 : fd_gossip_pull_response_t pull_response[ 1 ];
416 : fd_gossip_push_t push[ 1 ];
417 : fd_gossip_prune_t prune[ 1 ];
418 : fd_gossip_ping_t ping[ 1 ];
419 : fd_gossip_pong_t pong[ 1 ];
420 : };
421 : };
422 :
423 : typedef struct fd_gossip_message fd_gossip_message_t;
424 :
425 : static inline ulong
426 0 : fd_gossip_message_used_sz( fd_gossip_message_t const * message ) {
427 0 : switch( message->tag ) {
428 0 : case FD_GOSSIP_MESSAGE_PULL_RESPONSE: return (ulong)((uchar const *)message->pull_response->values-(uchar const *)message)+message->pull_response->values_len*sizeof(fd_gossip_value_t);
429 0 : case FD_GOSSIP_MESSAGE_PUSH: return (ulong)((uchar const *)message->push->values -(uchar const *)message)+message->push->values_len *sizeof(fd_gossip_value_t);
430 0 : case FD_GOSSIP_MESSAGE_PULL_REQUEST: return (ulong)((uchar const *)(message->pull_request+1)-(uchar const *)message);
431 0 : case FD_GOSSIP_MESSAGE_PRUNE: return (ulong)((uchar const *)(message->prune+1) -(uchar const *)message);
432 0 : case FD_GOSSIP_MESSAGE_PING: return (ulong)((uchar const *)(message->ping+1) -(uchar const *)message);
433 0 : case FD_GOSSIP_MESSAGE_PONG: return (ulong)((uchar const *)(message->pong+1) -(uchar const *)message);
434 0 : default: return sizeof(fd_gossip_message_t);
435 0 : }
436 0 : }
437 :
438 : int
439 : fd_gossip_message_deserialize( fd_gossip_message_t * message,
440 : uchar const * payload,
441 : ulong payload_sz );
442 :
443 : long
444 : fd_gossip_value_serialize( fd_gossip_value_t const * value,
445 : uchar * out,
446 : ulong out_sz );
447 :
448 : struct fd_gossip_update_message {
449 : int tag;
450 :
451 : uchar origin[ 32UL ];
452 : ulong wallclock;
453 :
454 : union {
455 : struct {
456 : ulong idx;
457 : fd_gossip_contact_info_t value[ 1 ];
458 : } contact_info[ 1 ];
459 :
460 : struct {
461 : ulong idx;
462 : } contact_info_remove[ 1 ];
463 :
464 : fd_gossip_duplicate_shred_t duplicate_shred[ 1 ];
465 : fd_gossip_snapshot_hashes_t snapshot_hashes[ 1 ];
466 : };
467 : };
468 :
469 : typedef struct fd_gossip_update_message fd_gossip_update_message_t;
470 :
471 : long
472 : fd_gossip_pull_request_init( uchar * payload,
473 : ulong payload_sz,
474 : ulong num_keys,
475 : ulong num_bits,
476 : ulong mask,
477 : uint mask_bits,
478 : uchar const * contact_info_crds,
479 : ulong contact_info_crds_sz,
480 : uchar ** out_bloom_keys,
481 : uchar ** out_bloom_bits,
482 : uchar ** out_bits_set );
483 :
484 : /* fd_gossip_version_cstr converts gossip version fields to a null
485 : terminated c-string. Returns 1 on success and 0 on failure (e.g.
486 : small out_sz)
487 :
488 : The 16-bit minor field has a special encoding to support semver
489 : prerelease notation.
490 : - High 2 bits: prerelease channel (0=stable, 1=rc, 2=beta, 3=alpha)
491 : - Low 14 bits: actual minor version number
492 :
493 : Patch field semantics:
494 : - If prerelease_bits==0 (stable), `patch` is rendered as the
495 : normal semver patch component:
496 : `<major>.<minor_actual>.<patch>`.
497 : - If prerelease_bits!=0 (prerelease), the semver patch component
498 : is forced to 0 and `patch` is interpreted as the prerelease
499 : sequence number, rendered as:
500 : `<major>.<minor_actual>.0-<channel>.<patch>`.
501 :
502 : Note that due to Frankendancer's existing unique versioning hack,
503 : future Frankendancer releases will all be stable (i.e.
504 : prerelease_bits==0) and based on stable Agave versions. This is a
505 : permissible compromise given Frankendancer is approaching EOL. */
506 : static inline int
507 : fd_gossip_version_cstr( ushort major,
508 : ushort minor,
509 : ushort patch,
510 : char * out,
511 27 : ulong out_sz ) {
512 27 : ushort prerelease_bits = (minor >> 14U) & 0x3U;
513 27 : ushort minor_actual = minor & 0x3FFFU;
514 :
515 27 : if( FD_UNLIKELY( prerelease_bits ) ) {
516 0 : const char * names[] = { "", "rc", "beta", "alpha" };
517 0 : return fd_cstr_printf_check( out, out_sz, NULL, "%hu.%hu.0-%s.%hu", major, minor_actual, names[ prerelease_bits ], patch );
518 27 : } else {
519 : return fd_cstr_printf_check( out, out_sz, NULL, "%hu.%hu.%hu", major, minor_actual, patch );
520 27 : }
521 27 : }
522 :
523 : #endif /* HEADER_fd_src_flamenco_gossip_fd_gossip_message_h */
|