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