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 : struct fd_gossip_view_vote {
189 : uchar index;
190 : ulong txn_sz;
191 : ushort txn_off;
192 : };
193 :
194 : typedef struct fd_gossip_view_vote fd_gossip_view_vote_t;
195 :
196 : struct fd_gossip_view_epoch_slots {
197 : uchar index;
198 : };
199 :
200 : typedef struct fd_gossip_view_epoch_slots fd_gossip_view_epoch_slots_t;
201 :
202 : struct fd_gossip_view_duplicate_shred {
203 : ushort index;
204 : ulong slot;
205 : uchar num_chunks;
206 : uchar chunk_index;
207 : ulong chunk_len;
208 : ushort chunk_off;
209 : };
210 :
211 : typedef struct fd_gossip_view_duplicate_shred fd_gossip_view_duplicate_shred_t;
212 :
213 : typedef struct fd_gossip_view_snapshot_hash_pair fd_gossip_view_snapshot_hash_pair_t;
214 : struct fd_gossip_view_snapshot_hashes {
215 : ushort full_off; /* Offset to the full snapshot hashes (slot, hash) pair */
216 : ulong inc_len;
217 : ushort inc_off; /* Offset to start of incremental snapshot hashes pair */
218 : };
219 :
220 : typedef struct fd_gossip_view_snapshot_hashes fd_gossip_view_snapshot_hashes_t;
221 :
222 : /* Offsets are within full message payload, not the subset where the encoded
223 : CRDS value lies. */
224 : struct fd_gossip_view_crds_value {
225 : union {
226 : ushort value_off; /* Start of CRDS value data in payload */
227 : ushort signature_off;
228 : };
229 : ushort pubkey_off;
230 : long wallclock_nanos;
231 : ushort length; /* Length of the value in bytes (incl. signature) */
232 :
233 : uchar tag;
234 : union{
235 : fd_gossip_view_contact_info_t ci_view[ 1 ];
236 : fd_gossip_view_node_instance_t node_instance[ 1 ];
237 : fd_gossip_view_vote_t vote[ 1 ];
238 : fd_gossip_view_epoch_slots_t epoch_slots[ 1 ];
239 : fd_gossip_view_duplicate_shred_t duplicate_shred[ 1 ];
240 : ulong lowest_slot;
241 : fd_gossip_view_snapshot_hashes_t snapshot_hashes[ 1 ];
242 : };
243 : };
244 :
245 : typedef struct fd_gossip_view_crds_value fd_gossip_view_crds_value_t;
246 :
247 : struct fd_gossip_view_crds_container {
248 : ushort from_off;
249 : ushort crds_values_len;
250 :
251 : fd_gossip_view_crds_value_t crds_values[ FD_GOSSIP_MSG_MAX_CRDS ];
252 : };
253 :
254 : typedef struct fd_gossip_view_crds_container fd_gossip_view_crds_container_t;
255 : typedef struct fd_gossip_view_crds_container fd_gossip_view_pull_response_t;
256 : typedef struct fd_gossip_view_crds_container fd_gossip_view_push_t;
257 : struct fd_gossip_view_pull_request {
258 : ulong bloom_keys_len;
259 : ulong bloom_keys_offset;
260 :
261 : ulong bloom_len;
262 : ulong bloom_bits_offset;
263 : ulong bloom_bits_cnt;
264 :
265 : ulong bloom_num_bits_set;
266 : ulong mask;
267 : uint mask_bits;
268 :
269 : fd_gossip_view_crds_value_t pr_ci[ 1 ];
270 : };
271 :
272 : typedef struct fd_gossip_view_pull_request fd_gossip_view_pull_request_t;
273 :
274 : struct fd_gossip_view_prune {
275 : ushort pubkey_off;
276 : ulong origins_len;
277 : ushort origins_off;
278 : ushort destination_off;
279 : ulong wallclock;
280 : ushort signature_off;
281 :
282 : long wallclock_nanos;
283 : };
284 :
285 : typedef struct fd_gossip_view_prune fd_gossip_view_prune_t;
286 :
287 : /* Ping/Pong can be casted on to the payload bytes
288 : directly */
289 : struct __attribute__((__packed__)) fd_gossip_view_ping {
290 : uchar pubkey[ 32UL ];
291 : uchar ping_token[ 32UL ];
292 : uchar signature[ 64UL ];
293 : };
294 :
295 : typedef struct fd_gossip_view_ping fd_gossip_view_ping_t;
296 :
297 : struct __attribute__((__packed__)) fd_gossip_view_pong {
298 : uchar pubkey[ 32UL ];
299 : uchar ping_hash[ 32UL ];
300 : uchar signature[ 64UL ];
301 : };
302 :
303 : typedef struct fd_gossip_view_pong fd_gossip_view_pong_t;
304 :
305 : struct fd_gossip_view {
306 : uchar tag; // uint in rust bincode
307 : union {
308 : fd_gossip_view_pull_request_t pull_request[ 1 ];
309 : fd_gossip_view_pull_response_t pull_response[ 1 ];
310 : fd_gossip_view_push_t push[ 1 ];
311 : fd_gossip_view_prune_t prune[ 1 ];
312 : ushort ping_pong_off;
313 : };
314 : };
315 :
316 : typedef struct fd_gossip_view fd_gossip_view_t;
317 :
318 : static inline fd_ip4_port_t
319 : fd_contact_info_get_socket( fd_contact_info_t const * ci,
320 0 : uchar tag ) {
321 0 : if( FD_UNLIKELY( tag>=FD_CONTACT_INFO_SOCKET_CNT ) ) {
322 0 : FD_LOG_ERR(( "Invalid socket tag %u", tag ));
323 0 : }
324 0 : return ci->sockets[ tag ];
325 0 : }
326 :
327 : static inline fd_ip4_port_t
328 0 : fd_contact_info_gossip_socket( fd_contact_info_t const * ci ) {
329 0 : return fd_contact_info_get_socket( ci, FD_CONTACT_INFO_SOCKET_GOSSIP );
330 0 : }
331 :
332 : ulong
333 : fd_gossip_msg_parse( fd_gossip_view_t * view,
334 : uchar const * payload,
335 : ulong payload_sz );
336 :
337 : FD_FN_CONST static inline ulong
338 : fd_gossip_pull_request_max_filter_bits( ulong num_keys,
339 : ulong contact_info_crds_sz,
340 0 : ulong payload_sz ) {
341 0 : return 8UL*( payload_sz
342 0 : - 4UL /* discriminant */
343 0 : - 8UL /* keys len */
344 0 : - 8UL*num_keys /* keys */
345 0 : - 1UL /* has_bits */
346 0 : - 8UL /* bloom vec len */
347 0 : - 8UL /* bloom bits count */
348 0 : - 8UL /* bloom num bits set */
349 0 : - 8UL /* mask */
350 0 : - 4UL /* mask bits */
351 0 : - contact_info_crds_sz ); /* contact info CRDS val */
352 0 : }
353 :
354 : int
355 : fd_gossip_pull_request_init( uchar * payload,
356 : ulong payload_sz,
357 : ulong num_keys,
358 : ulong num_bits,
359 : ulong mask,
360 : uint mask_bits,
361 : uchar const * contact_info_crds,
362 : ulong contact_info_crds_sz,
363 : ulong ** out_bloom_keys,
364 : ulong ** out_bloom_bits,
365 : ulong ** out_bits_set,
366 : ulong * out_payload_sz );
367 :
368 : int
369 : fd_gossip_contact_info_encode( fd_contact_info_t const * contact_info,
370 : uchar * out_buf,
371 : ulong out_buf_cap,
372 : ulong * opt_encoded_sz );
373 :
374 : int
375 : fd_gossip_crds_vote_encode( uchar * out_buf,
376 : ulong out_buf_sz,
377 : uchar const * txn,
378 : ulong txn_sz,
379 : uchar const * identity_pubkey,
380 : long now,
381 : ulong * opt_encoded_sz );
382 : #endif /* HEADER_fd_src_flamenco_gossip_fd_gossip_private_h */
|