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