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 :
58 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)
59 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)
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 0 : #define FD_GOSSIP_UPDATE_TAG_CONTACT_INFO (0)
76 0 : #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 0 : #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 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_SOLANA_LABS (0)
100 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_JITO_LABS (1)
101 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_FRANKENDANCER (2)
102 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_AGAVE (3)
103 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_AGAVE_PALADIN (4)
104 0 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_FIREDANCER (5)
105 : #define FD_GOSSIP_CONTACT_INFO_CLIENT_AGAVE_BAM (6)
106 : #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_value {
228 : uint tag;
229 :
230 : uchar signature[ 64UL ];
231 : uchar origin[ 32UL ];
232 : ulong wallclock;
233 :
234 : ulong offset;
235 : ulong length;
236 :
237 : union {
238 : // DEPRECATED OR UNUSED
239 : // fd_gossip_legacy_contact_info_t legacy_contact_info[ 1 ];
240 : // fd_gossip_lowest_slot_t lowest_slot[ 1 ];
241 : // fd_gossip_legacy_snapshot_hashes_t legacy_snapshot_hashes[ 1 ];
242 : // fd_gossip_account_hashes_t account_hashes[ 1 ];
243 : // fd_gossip_legacy_version_t legacy_version[ 1 ];
244 : // fd_gossip_version_t version[ 1 ];
245 : // fd_gossip_restart_last_voted_fork_slots_t restart_last_voted_fork_slots[ 1 ];
246 : // fd_gossip_restart_heaviest_fork_t restart_heaviest_fork[ 1 ];
247 :
248 : fd_gossip_vote_t vote[ 1 ];
249 : fd_gossip_node_instance_t node_instance[ 1 ];
250 : fd_gossip_duplicate_shred_t duplicate_shred[ 1 ];
251 : fd_gossip_snapshot_hashes_t snapshot_hashes[ 1 ];
252 : fd_gossip_contact_info_t contact_info[ 1 ];
253 : fd_gossip_epoch_slots_t epoch_slots[ 1 ];
254 : };
255 : };
256 :
257 : typedef struct fd_gossip_value fd_gossip_value_t;
258 :
259 : /* Tightest bounds for Bloom keys[]/bits[] given network constraints.
260 :
261 : IPv6 minimum MTU = 1280
262 : IPv6 header = 40
263 : UDP header = 8
264 : PACKET_DATA_SIZE = 1232 (= 1280 - 40 - 8)
265 :
266 : PullRequest is the only message containing a Bloom filter.
267 :
268 : Bytes consumed before keys loop:
269 : Protocol tag(4) + keys_len(8) = 12
270 :
271 : Remaining: 1232 - 12 = 1220
272 : Each key: 8 bytes
273 : Max keys = floor(1220/8) = 152
274 :
275 : Bytes consumed before bits data:
276 : Protocol tag(4) + keys_len(8) + has_bits(1) +
277 : bits_cap(8) = 21
278 :
279 : Remaining: 1232 - 21 = 1211
280 : Each u64: 8 bytes
281 : Max bits = floor(1211/8) = 151 */
282 :
283 : struct fd_gossip_bloom {
284 : ulong keys_len;
285 : ulong keys[ 152UL ];
286 : ulong bits_cap;
287 : ulong bits_len;
288 : ulong bits[ 151UL ];
289 : ulong num_bits_set;
290 : };
291 :
292 : typedef struct fd_gossip_bloom fd_gossip_bloom_t;
293 :
294 : struct fd_gossip_crds_filter {
295 : fd_gossip_bloom_t filter[ 1 ];
296 : ulong mask;
297 : uint mask_bits;
298 : };
299 :
300 : typedef struct fd_gossip_crds_filter fd_gossip_crds_filter_t;
301 :
302 : struct fd_gossip_pull_request {
303 : fd_gossip_crds_filter_t crds_filter[ 1 ];
304 :
305 : fd_gossip_value_t contact_info[ 1 ];
306 : };
307 :
308 : typedef struct fd_gossip_pull_request fd_gossip_pull_request_t;
309 :
310 : struct fd_gossip_pull_response {
311 : uchar from[ 32UL ];
312 : ulong values_len;
313 : fd_gossip_value_t values[ FD_GOSSIP_MESSAGE_MAX_CRDS ];
314 : };
315 :
316 : typedef struct fd_gossip_pull_response fd_gossip_pull_response_t;
317 :
318 : struct fd_gossip_push {
319 : uchar from[ 32UL ];
320 : ulong values_len;
321 : fd_gossip_value_t values[ FD_GOSSIP_MESSAGE_MAX_CRDS ];
322 : };
323 :
324 : typedef struct fd_gossip_push fd_gossip_push_t;
325 :
326 : /* Tightest bound for prunes[] given network constraints.
327 :
328 : IPv6 minimum MTU = 1280
329 : IPv6 header = 40
330 : UDP header = 8
331 : PACKET_DATA_SIZE = 1232 (= 1280 - 40 - 8)
332 :
333 : Bytes consumed before prunes loop:
334 : Protocol tag(4) + sender(32) + pubkey(32) +
335 : prunes_len(8) = 76
336 :
337 : Remaining: 1232 - 76 = 1156
338 : Each prune: 32 bytes
339 : Max prunes = floor(1156/32) = 36 */
340 :
341 : struct fd_gossip_prune {
342 : uchar sender[ 32UL ];
343 : uchar pubkey[ 32UL ];
344 : ulong prunes_len;
345 : uchar prunes[ 36UL ][ 32UL ];
346 : uchar signature[ 64UL ];
347 : uchar destination[ 32UL ];
348 : ulong wallclock;
349 : };
350 :
351 : typedef struct fd_gossip_prune fd_gossip_prune_t;
352 :
353 : struct fd_gossip_ping {
354 : uchar from[ 32UL ];
355 : uchar token[ 32UL ];
356 : uchar signature[ 64UL ];
357 : };
358 :
359 : typedef struct fd_gossip_ping fd_gossip_ping_t;
360 :
361 : struct fd_gossip_pong {
362 : uchar from[ 32UL ];
363 : uchar hash[ 32UL ];
364 : uchar signature[ 64UL ];
365 : };
366 :
367 : typedef struct fd_gossip_pong fd_gossip_pong_t;
368 :
369 : struct fd_gossip_message {
370 : uint tag;
371 :
372 : union {
373 : fd_gossip_pull_request_t pull_request[ 1 ];
374 : fd_gossip_pull_response_t pull_response[ 1 ];
375 : fd_gossip_push_t push[ 1 ];
376 : fd_gossip_prune_t prune[ 1 ];
377 : fd_gossip_ping_t ping[ 1 ];
378 : fd_gossip_pong_t pong[ 1 ];
379 : };
380 : };
381 :
382 : typedef struct fd_gossip_message fd_gossip_message_t;
383 :
384 : int
385 : fd_gossip_message_deserialize( fd_gossip_message_t * message,
386 : uchar const * payload,
387 : ulong payload_sz );
388 :
389 : long
390 : fd_gossip_value_serialize( fd_gossip_value_t const * value,
391 : uchar * out,
392 : ulong out_sz );
393 :
394 : struct fd_gossip_update_message {
395 : int tag;
396 :
397 : uchar origin[ 32UL ];
398 : ulong wallclock;
399 :
400 : union {
401 : struct {
402 : ulong idx;
403 : fd_gossip_contact_info_t value[ 1 ];
404 : } contact_info[ 1 ];
405 :
406 : struct {
407 : ulong idx;
408 : } contact_info_remove[ 1 ];
409 :
410 : struct {
411 : fd_gossip_socket_t socket[ 1 ];
412 : fd_gossip_vote_t value[ 1 ];
413 : } vote[ 1 ];
414 :
415 : fd_gossip_duplicate_shred_t duplicate_shred[ 1 ];
416 : fd_gossip_snapshot_hashes_t snapshot_hashes[ 1 ];
417 : };
418 : };
419 :
420 : typedef struct fd_gossip_update_message fd_gossip_update_message_t;
421 :
422 : long
423 : fd_gossip_pull_request_init( uchar * payload,
424 : ulong payload_sz,
425 : ulong num_keys,
426 : ulong num_bits,
427 : ulong mask,
428 : uint mask_bits,
429 : uchar const * contact_info_crds,
430 : ulong contact_info_crds_sz,
431 : ulong ** out_bloom_keys,
432 : ulong ** out_bloom_bits,
433 : ulong ** out_bits_set );
434 :
435 : /* fd_gossip_version_cstr converts gossip version fields to a null
436 : terminated c-string. Returns 1 on success and 0 on failure (e.g.
437 : small out_sz)
438 :
439 : The 16-bit minor field has a special encoding to support semver
440 : prerelease notation.
441 : - High 2 bits: prerelease channel (0=stable, 1=rc, 2=beta, 3=alpha)
442 : - Low 14 bits: actual minor version number
443 :
444 : Patch field semantics:
445 : - If prerelease_bits==0 (stable), `patch` is rendered as the
446 : normal semver patch component:
447 : `<major>.<minor_actual>.<patch>`.
448 : - If prerelease_bits!=0 (prerelease), the semver patch component
449 : is forced to 0 and `patch` is interpreted as the prerelease
450 : sequence number, rendered as:
451 : `<major>.<minor_actual>.0-<channel>.<patch>`.
452 :
453 : Note that due to Frankendancer's existing unique versioning hack,
454 : future Frankendancer releases will all be stable (i.e.
455 : prerelease_bits==0) and based on stable Agave versions. This is a
456 : permissible compromise given Frankendancer is approaching EOL. */
457 : static inline int
458 : fd_gossip_version_cstr( ushort major,
459 : ushort minor,
460 : ushort patch,
461 : char * out,
462 0 : ulong out_sz ) {
463 0 : ushort prerelease_bits = (minor >> 14U) & 0x3U;
464 0 : ushort minor_actual = minor & 0x3FFFU;
465 :
466 0 : if( FD_UNLIKELY( prerelease_bits ) ) {
467 0 : const char * names[] = { "", "rc", "beta", "alpha" };
468 0 : return fd_cstr_printf_check( out, out_sz, NULL, "%hu.%hu.0-%s.%hu", major, minor_actual, names[ prerelease_bits ], patch );
469 0 : } else {
470 0 : return fd_cstr_printf_check( out, out_sz, NULL, "%hu.%hu.%hu", major, minor_actual, patch );
471 0 : }
472 0 : }
473 :
474 : #endif /* HEADER_fd_src_flamenco_gossip_fd_gossip_message_h */
|