Line data Source code
1 :
2 : #ifndef HEADER_fd_src_flamenco_gossip_fd_gossip_private_h
3 : #define HEADER_fd_src_flamenco_gossip_fd_gossip_private_h
4 :
5 : #include "fd_gossip_types.h"
6 : #include "../../util/fd_util.h"
7 : #include "../../disco/fd_disco_base.h"
8 :
9 : #include <stddef.h> // offsetof
10 :
11 : /* Constants used in deriving size bounds
12 : - 1232b (MTU)
13 : - 1188b = 1232b-4b(discriminant)-32b(pubkey)-8(crds len) max CRDS sz
14 : largest CRDS value seen so far is duplicate shreds at 1187b*/
15 :
16 3 : #define FD_GOSSIP_CRDS_MAX_SZ (1188UL)
17 :
18 : /* Deriving maximum number of CRDS values a message can hold:
19 : - Each CRDS value contains a 64b signature and a 4b discriminant.
20 : So, each CRDS value is at least 68b.
21 : - Smallest CRDS data is technically slot hashes with zero hashes,
22 : which is 32b (pubkey) + 8b (wallclock) + 8b (vector len), bringing
23 : total to 68b+32b+8b+8b=116b
24 : - However, we take a more conservative approach and assume just the
25 : signature and discriminant.
26 : - So, maximum number of CRDS values is 1188/(68) ~= 18 */
27 0 : #define FD_GOSSIP_MSG_MAX_CRDS (18UL)
28 :
29 0 : #define FD_GOSSIP_MESSAGE_PULL_REQUEST (0)
30 0 : #define FD_GOSSIP_MESSAGE_PULL_RESPONSE (1)
31 0 : #define FD_GOSSIP_MESSAGE_PUSH (2)
32 0 : #define FD_GOSSIP_MESSAGE_PRUNE (3)
33 0 : #define FD_GOSSIP_MESSAGE_PING (4)
34 0 : #define FD_GOSSIP_MESSAGE_PONG (5)
35 : #define FD_GOSSIP_MESSAGE_LAST (FD_GOSSIP_MESSAGE_PONG)
36 :
37 0 : #define FD_GOSSIP_VALUE_LEGACY_CONTACT_INFO ( 0)
38 0 : #define FD_GOSSIP_VALUE_VOTE ( 1)
39 0 : #define FD_GOSSIP_VALUE_LOWEST_SLOT ( 2) // SOME FIELDS DEPRECATED
40 0 : #define FD_GOSSIP_VALUE_LEGACY_SNAPSHOT_HASHES ( 3) // DEPRECATED
41 0 : #define FD_GOSSIP_VALUE_ACCOUNT_HASHES ( 4) // DEPRECATED
42 0 : #define FD_GOSSIP_VALUE_EPOCH_SLOTS ( 5)
43 0 : #define FD_GOSSIP_VALUE_LEGACY_VERSION ( 6)
44 0 : #define FD_GOSSIP_VALUE_VERSION ( 7)
45 0 : #define FD_GOSSIP_VALUE_NODE_INSTANCE ( 8)
46 0 : #define FD_GOSSIP_VALUE_DUPLICATE_SHRED ( 9)
47 0 : #define FD_GOSSIP_VALUE_INC_SNAPSHOT_HASHES (10)
48 450 : #define FD_GOSSIP_VALUE_CONTACT_INFO (11)
49 0 : #define FD_GOSSIP_VALUE_RESTART_LAST_VOTED_FORK_SLOTS (12)
50 0 : #define FD_GOSSIP_VALUE_RESTART_HEAVIEST_FORK (13)
51 : #define FD_GOSSIP_VALUE_LAST (FD_GOSSIP_VALUE_RESTART_HEAVIEST_FORK)
52 :
53 : /* Gossip messages encode wallclock in millis*, while we
54 : parse them into nanoseconds for internal use.
55 :
56 : * exceptions:
57 : - Contact Info outset (AKA instance creation wallclock) is encoded
58 : in micros */
59 3 : #define FD_NANOSEC_TO_MILLI(_ts_) ((long)(_ts_/1000000))
60 0 : #define FD_MILLI_TO_NANOSEC(_ts_) ((long)(_ts_*1000000))
61 3 : #define FD_NANOSEC_TO_MICRO(_ts_) ((long)(_ts_/1000))
62 0 : #define FD_MICRO_TO_NANOSEC(_ts_) ((long)(_ts_*1000))
63 :
64 : /* Bound max inc entries by
65 : 1188b (max CRDS encoded buffer size )
66 : - 64b (signature)
67 : - 4b (tag)
68 : - 32b (pubkey)
69 : - 40b (full pair)
70 : - 8b (inc len)
71 : - 8b (wallclock)
72 : = 1032b
73 :
74 : 1032b/40 ~= 25 */
75 : FD_STATIC_ASSERT( FD_GOSSIP_SNAPSHOT_HASHES_MAX_INCREMENTAL==25UL,
76 : "FD_GOSSIP_SNAPSHOT_HASHES_MAX_INCREMENTAL must be 25" );
77 :
78 :
79 0 : #define FD_GOSSIP_UPDATE_SZ_CONTACT_INFO (offsetof(fd_gossip_update_message_t, contact_info) + sizeof(ulong) + sizeof(fd_contact_info_t))
80 0 : #define FD_GOSSIP_UPDATE_SZ_CONTACT_INFO_REMOVE (offsetof(fd_gossip_update_message_t, contact_info_remove) + sizeof(ulong))
81 0 : #define FD_GOSSIP_UPDATE_SZ_LOWEST_SLOT (offsetof(fd_gossip_update_message_t, lowest_slot) + sizeof(ulong))
82 0 : #define FD_GOSSIP_UPDATE_SZ_VOTE (offsetof(fd_gossip_update_message_t, vote) + sizeof(fd_gossip_vote_t))
83 0 : #define FD_GOSSIP_UPDATE_SZ_DUPLICATE_SHRED (offsetof(fd_gossip_update_message_t, duplicate_shred) + sizeof(fd_gossip_duplicate_shred_t))
84 0 : #define FD_GOSSIP_UPDATE_SZ_SNAPSHOT_HASHES (offsetof(fd_gossip_update_message_t, snapshot_hashes) + sizeof(fd_gossip_snapshot_hashes_t))
85 :
86 : struct fd_gossip_view_ipaddr {
87 : uchar is_ip6;
88 : union {
89 : uint ip4;
90 : ushort ip6_off;
91 : };
92 : };
93 :
94 : typedef struct fd_gossip_view_ipaddr fd_gossip_view_ipaddr_t;
95 :
96 : struct fd_gossip_view_socket {
97 : uchar key;
98 : uchar index;
99 : ushort offset; /* NOTE: this is a varint in encoded form */
100 : };
101 :
102 : typedef struct fd_gossip_view_socket fd_gossip_view_socket_t;
103 :
104 : /* To get the minimum possible wire size of a Version message, we use
105 : version 0.0.0 client 0 (anything less than 128 works):
106 :
107 : 1b (major)
108 : + 1b (minor)
109 : + 1b (patch)
110 : + 4b (commit)
111 : + 4b (feature set)
112 : + 1b (client)
113 : = 12b */
114 : struct fd_gossip_view_version {
115 : ushort major;
116 : ushort minor;
117 : ushort patch;
118 : uint commit; /* First 4 bytes of the commit hash */
119 : uint feature_set; /* Feature set encoded as a bitmask */
120 : ushort client;
121 : };
122 :
123 : typedef struct fd_gossip_view_version fd_gossip_view_version_t;
124 :
125 : /* Contact info size bound calculations:
126 :
127 : The minimal valid contact info would hold 0 addrs, 0 sockets,
128 : and an empty extensions array. This ends up taking
129 :
130 : 32b (pubkey)
131 : + 8b (wallclock)
132 : + 8b (outset)
133 : + 2b (shred version)
134 : + 12b (minimum version)
135 : + 1b (addrs_len)
136 : + 1b (sockets_len)
137 : + 1b (ext_len)
138 : = 65b
139 :
140 : This leaves us with 1188b - 65b = 1123b to hold addrs, sockets or
141 : extensions. Extension is just a byte array, so we can ignore sizing it
142 : and instead offset it in the payload.
143 :
144 : Before analyzing size bounds for addrs and sockets, we establish the
145 : minimum size socket entry:
146 : 1b (key)
147 : + 1b (index)
148 : + 1b (offset, compact-u16)
149 : = 3b
150 :
151 : According to Agave's ContactInfo verifier (linked below), every IP
152 : address must be unique, and must be referenced by at least one socket.
153 : This means that the number of addrs must be at most the number of
154 : sockets. So to find the maximum n (addr, socket) pairs we can fit in
155 : 1123b:
156 : 1123b / (8b (addr) + 3b (socket)) ~= 102 pairs.
157 :
158 : This bounds the number of addrs to 102. We cannot apply this bound to
159 : sockets, because the socket entries can reference the same addr
160 : multiple times, so we can have just 1 addr and use the remaining space
161 : to hold sockets.
162 :
163 : Agave's verifier enforces a unique socket tag (key) across all
164 : sockets, and since the key is 1b, this bounds us to 256 sockets.
165 :
166 : https://github.com/anza-xyz/agave/blob/540d5bc56cd44e3cc61b179bd52e9a782a2c99e4/gossip/src/contact_info.rs#L599-L643 */
167 :
168 : #define FD_GOSSIP_CONTACT_INFO_MAX_ADDRESSES (102UL)
169 : #define FD_GOSSIP_CONTACT_INFO_MAX_SOCKETS (256UL)
170 :
171 : struct fd_gossip_view_contact_info {
172 : fd_contact_info_t contact_info[1];
173 : ulong ip6_cnt;
174 : ulong unrecognized_socket_tag_cnt;
175 :
176 : ushort ext_len;
177 : ushort ext_off;
178 : };
179 :
180 : typedef struct fd_gossip_view_contact_info fd_gossip_view_contact_info_t;
181 :
182 : struct fd_gossip_view_node_instance {
183 : ulong token;
184 : };
185 :
186 : typedef struct fd_gossip_view_node_instance fd_gossip_view_node_instance_t;
187 :
188 : #define FD_GOSSIP_VOTE_IDX_MAX (32U)
189 : struct fd_gossip_view_vote {
190 : uchar index;
191 : ulong txn_sz;
192 : ushort txn_off;
193 : };
194 :
195 : typedef struct fd_gossip_view_vote fd_gossip_view_vote_t;
196 :
197 : #define FD_GOSSIP_EPOCH_SLOTS_IDX_MAX (255U)
198 : struct fd_gossip_view_epoch_slots {
199 : uchar index;
200 : };
201 :
202 : typedef struct fd_gossip_view_epoch_slots fd_gossip_view_epoch_slots_t;
203 :
204 : #define FD_GOSSIP_DUPLICATE_SHRED_IDX_MAX (512U)
205 : struct fd_gossip_view_duplicate_shred {
206 : ushort index;
207 : ulong slot;
208 : uchar num_chunks;
209 : uchar chunk_index;
210 : ulong chunk_len;
211 : ushort chunk_off;
212 : };
213 :
214 : typedef struct fd_gossip_view_duplicate_shred fd_gossip_view_duplicate_shred_t;
215 :
216 : typedef struct fd_gossip_view_snapshot_hash_pair fd_gossip_view_snapshot_hash_pair_t;
217 : struct fd_gossip_view_snapshot_hashes {
218 : ushort full_off; /* Offset to the full snapshot hashes (slot, hash) pair */
219 : ulong inc_len;
220 : ushort inc_off; /* Offset to start of incremental snapshot hashes pair */
221 : };
222 :
223 : typedef struct fd_gossip_view_snapshot_hashes fd_gossip_view_snapshot_hashes_t;
224 :
225 : /* Offsets are within full message payload, not the subset where the encoded
226 : CRDS value lies. */
227 : struct fd_gossip_view_crds_value {
228 : union {
229 : ushort value_off; /* Start of CRDS value data in payload */
230 : ushort signature_off;
231 : };
232 : ushort pubkey_off;
233 : long wallclock_nanos;
234 : ushort length; /* Length of the value in bytes (incl. signature) */
235 :
236 : uchar tag;
237 : union{
238 : fd_gossip_view_contact_info_t ci_view[ 1 ];
239 : fd_gossip_view_node_instance_t node_instance[ 1 ];
240 : fd_gossip_view_vote_t vote[ 1 ];
241 : fd_gossip_view_epoch_slots_t epoch_slots[ 1 ];
242 : fd_gossip_view_duplicate_shred_t duplicate_shred[ 1 ];
243 : ulong lowest_slot;
244 : fd_gossip_view_snapshot_hashes_t snapshot_hashes[ 1 ];
245 : };
246 : };
247 :
248 : typedef struct fd_gossip_view_crds_value fd_gossip_view_crds_value_t;
249 :
250 : struct fd_gossip_view_crds_container {
251 : ushort from_off;
252 : ushort crds_values_len;
253 :
254 : fd_gossip_view_crds_value_t crds_values[ FD_GOSSIP_MSG_MAX_CRDS ];
255 : };
256 :
257 : typedef struct fd_gossip_view_crds_container fd_gossip_view_crds_container_t;
258 : typedef struct fd_gossip_view_crds_container fd_gossip_view_pull_response_t;
259 : typedef struct fd_gossip_view_crds_container fd_gossip_view_push_t;
260 : struct fd_gossip_view_pull_request {
261 : ulong bloom_keys_len;
262 : ulong bloom_keys_offset;
263 :
264 : ulong bloom_len;
265 : ulong bloom_bits_offset;
266 : ulong bloom_bits_cnt;
267 :
268 : ulong bloom_num_bits_set;
269 : ulong mask;
270 : uint mask_bits;
271 :
272 : fd_gossip_view_crds_value_t pr_ci[ 1 ];
273 : };
274 :
275 : typedef struct fd_gossip_view_pull_request fd_gossip_view_pull_request_t;
276 :
277 : struct fd_gossip_view_prune {
278 : ushort pubkey_off;
279 : ulong origins_len;
280 : ushort origins_off;
281 : ushort destination_off;
282 : ulong wallclock;
283 : ushort signature_off;
284 :
285 : long wallclock_nanos;
286 : };
287 :
288 : typedef struct fd_gossip_view_prune fd_gossip_view_prune_t;
289 :
290 : /* Ping/Pong can be casted on to the payload bytes
291 : directly */
292 : struct __attribute__((__packed__)) fd_gossip_view_ping {
293 : uchar pubkey[ 32UL ];
294 : uchar ping_token[ 32UL ];
295 : uchar signature[ 64UL ];
296 : };
297 :
298 : typedef struct fd_gossip_view_ping fd_gossip_view_ping_t;
299 :
300 : struct __attribute__((__packed__)) fd_gossip_view_pong {
301 : uchar pubkey[ 32UL ];
302 : uchar ping_hash[ 32UL ];
303 : uchar signature[ 64UL ];
304 : };
305 :
306 : typedef struct fd_gossip_view_pong fd_gossip_view_pong_t;
307 :
308 : struct fd_gossip_view {
309 : uchar tag; // uint in rust bincode
310 : union {
311 : fd_gossip_view_pull_request_t pull_request[ 1 ];
312 : fd_gossip_view_pull_response_t pull_response[ 1 ];
313 : fd_gossip_view_push_t push[ 1 ];
314 : fd_gossip_view_prune_t prune[ 1 ];
315 : ushort ping_pong_off;
316 : };
317 : };
318 :
319 : typedef struct fd_gossip_view fd_gossip_view_t;
320 :
321 : static inline fd_ip4_port_t
322 : fd_contact_info_get_socket( fd_contact_info_t const * ci,
323 0 : uchar tag ) {
324 0 : if( FD_UNLIKELY( tag>=FD_CONTACT_INFO_SOCKET_CNT ) ) {
325 0 : FD_LOG_ERR(( "Invalid socket tag %u", tag ));
326 0 : }
327 0 : return ci->sockets[ tag ];
328 0 : }
329 :
330 : static inline fd_ip4_port_t
331 0 : fd_contact_info_gossip_socket( fd_contact_info_t const * ci ) {
332 0 : return fd_contact_info_get_socket( ci, FD_CONTACT_INFO_SOCKET_GOSSIP );
333 0 : }
334 :
335 : ulong
336 : fd_gossip_msg_parse( fd_gossip_view_t * view,
337 : uchar const * payload,
338 : ulong payload_sz );
339 :
340 : FD_FN_CONST static inline ulong
341 : fd_gossip_pull_request_max_filter_bits( ulong num_keys,
342 : ulong contact_info_crds_sz,
343 0 : ulong payload_sz ) {
344 0 : return 8UL*( payload_sz
345 0 : - 4UL /* discriminant */
346 0 : - 8UL /* keys len */
347 0 : - 8UL*num_keys /* keys */
348 0 : - 1UL /* has_bits */
349 0 : - 8UL /* bloom vec len */
350 0 : - 8UL /* bloom bits count */
351 0 : - 8UL /* bloom num bits set */
352 0 : - 8UL /* mask */
353 0 : - 4UL /* mask bits */
354 0 : - contact_info_crds_sz ); /* contact info CRDS val */
355 0 : }
356 :
357 : int
358 : fd_gossip_pull_request_init( uchar * payload,
359 : ulong payload_sz,
360 : ulong num_keys,
361 : ulong num_bits,
362 : ulong mask,
363 : uint mask_bits,
364 : uchar const * contact_info_crds,
365 : ulong contact_info_crds_sz,
366 : ulong ** out_bloom_keys,
367 : ulong ** out_bloom_bits,
368 : ulong ** out_bits_set,
369 : ulong * out_payload_sz );
370 :
371 : int
372 : fd_gossip_contact_info_encode( fd_contact_info_t const * contact_info,
373 : uchar * out_buf,
374 : ulong out_buf_cap,
375 : ulong * opt_encoded_sz );
376 :
377 : int
378 : fd_gossip_crds_vote_encode( uchar * out_buf,
379 : ulong out_buf_sz,
380 : uchar const * txn,
381 : ulong txn_sz,
382 : uchar const * identity_pubkey,
383 : long now,
384 : ulong * opt_encoded_sz );
385 : #endif /* HEADER_fd_src_flamenco_gossip_fd_gossip_private_h */
|