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