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 */
|