Line data Source code
1 : #include "fd_gossip_message.h"
2 :
3 : #include <string.h>
4 :
5 : #include "../../ballet/txn/fd_compact_u16.h"
6 : #include "../runtime/fd_system_ids.h"
7 : #include "../runtime/program/vote/fd_vote_codec.h"
8 :
9 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/gossip/src/crds_data.rs#L22-L23 */
10 : #define WALLCLOCK_MAX_MILLIS (1000000000000000UL)
11 : #define MAX_SLOT (1000000000000000UL)
12 :
13 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/gossip/src/epoch_slots.rs#L16 */
14 : #define MAX_SLOTS_PER_EPOCH_SLOT (2048UL*8UL)
15 :
16 : #define FD_GOSSIP_VOTE_IDX_MAX (32)
17 : #define FD_GOSSIP_EPOCH_SLOTS_IDX_MAX (255U)
18 : #define FD_GOSSIP_DUPLICATE_SHRED_IDX_MAX (512U)
19 :
20 : /* Agave computes this threshold as:
21 : * mask_bits( MIN_NUM_BLOOM_ITEMS, max_items( PACKET_DATA_SIZE*8, FALSE_RATE, KEYS ) )
22 : * where mask_bits(n, m) = ceil(log2(n/m)). The derivation uses all protocol
23 : * constants so the result (6) is a protocol constant too.
24 : * https://github.com/anza-xyz/agave/blob/v4.2.0-beta.0/gossip/src/crds_gossip_pull.rs#L71-L79 */
25 : #define FD_GOSSIP_MIN_PULL_REQUEST_MASK_BITS (6U)
26 :
27 83434 : #define CHECK( cond ) do { \
28 83410 : if( FD_UNLIKELY( !(cond) ) ) return 0; \
29 83410 : } while( 0 )
30 :
31 20748 : #define READ_BYTES( dst, n, payload, payload_sz ) do { \
32 20748 : CHECK( (n)<=(*(payload_sz)) ); \
33 20748 : fd_memcpy( (dst), *(payload), (n) ); \
34 20708 : *(payload) += (n); \
35 20708 : *(payload_sz) -= (n); \
36 20708 : } while( 0 )
37 :
38 373 : #define SKIP_BYTES( n, payload, payload_sz ) do { \
39 373 : CHECK( (n)<=(*(payload_sz)) ); \
40 373 : *(payload) += (n); \
41 372 : *(payload_sz) -= (n); \
42 372 : } while( 0 )
43 :
44 633 : #define READ_OPTION( dst, payload, payload_sz ) do { \
45 633 : READ_U8( dst, payload, payload_sz ); \
46 633 : CHECK( (dst)==0 || (dst)==1 ); \
47 633 : } while( 0 )
48 :
49 6946 : #define READ_ENUM( dst, n, payload, payload_sz ) do { \
50 6946 : CHECK( 4UL<=(*(payload_sz)) ); \
51 6946 : (dst) = FD_LOAD( uint, *(payload) ); \
52 6946 : CHECK( (dst)<n ); \
53 6946 : *(payload) += 4UL; \
54 6440 : *(payload_sz) -= 4UL; \
55 6440 : } while( 0 )
56 :
57 1566 : #define READ_U8( dst, payload, payload_sz ) do { \
58 1566 : CHECK( 1UL<=(*(payload_sz)) ); \
59 1566 : (dst) = FD_LOAD( uchar, *(payload) ); \
60 1566 : *(payload) += 1UL; \
61 1566 : *(payload_sz) -= 1UL; \
62 1566 : } while( 0 )
63 :
64 158 : #define READ_U16( dst, payload, payload_sz ) do { \
65 158 : CHECK( 2UL<=(*(payload_sz)) ); \
66 158 : (dst) = FD_LOAD( ushort, *(payload) ); \
67 158 : *(payload) += 2UL; \
68 158 : *(payload_sz) -= 2UL; \
69 158 : } while( 0 )
70 :
71 235 : #define READ_U32( dst, payload, payload_sz ) do { \
72 235 : CHECK( 4UL<=(*(payload_sz)) ); \
73 235 : (dst) = FD_LOAD( uint, *(payload) ); \
74 235 : *(payload) += 4UL; \
75 235 : *(payload_sz) -= 4UL; \
76 235 : } while( 0 )
77 :
78 27115 : #define READ_U64( dst, payload, payload_sz ) do { \
79 27115 : CHECK( 8UL<=(*(payload_sz)) ); \
80 27115 : (dst) = FD_LOAD( ulong, *(payload) ); \
81 26858 : *(payload) += 8UL; \
82 26858 : *(payload_sz) -= 8UL; \
83 26858 : } while( 0 )
84 :
85 988 : #define READ_U16_VARINT( dst, payload, payload_sz ) do { \
86 988 : ulong _sz = fd_cu16_dec_sz( *(payload), *(payload_sz) ); \
87 988 : CHECK( _sz ); \
88 988 : (dst) = fd_cu16_dec_fixed( *(payload), _sz ); \
89 971 : *(payload) += _sz; \
90 971 : *(payload_sz) -= _sz; \
91 971 : } while( 0 )
92 :
93 84 : #define READ_U64_VARINT( dst, payload, payload_sz ) do { \
94 84 : ulong _val = 0UL; \
95 84 : uint _shift = 0U; \
96 467 : for(;;) { \
97 467 : CHECK( 1UL<=(*(payload_sz)) ); \
98 467 : uchar _byte = FD_LOAD( uchar, *(payload) ); \
99 467 : *(payload) += 1UL; \
100 467 : *(payload_sz) -= 1UL; \
101 467 : _val |= (ulong)(_byte & 0x7F) << _shift; \
102 467 : if( FD_LIKELY( !(_byte & 0x80) ) ) { \
103 84 : CHECK( (_val>>_shift)==(ulong)_byte ); /* last byte not truncated */ \
104 84 : CHECK( _byte || !_shift ); /* no trailing zero bytes */ \
105 84 : (dst) = _val; \
106 84 : break; \
107 84 : } \
108 467 : _shift += 7U; \
109 383 : CHECK( _shift<64U ); \
110 383 : } \
111 84 : } while( 0 )
112 :
113 1009 : #define READ_WALLCLOCK( dst, payload, payload_sz ) do { \
114 1009 : ulong wallclock_millis; \
115 1009 : READ_U64( wallclock_millis, payload, payload_sz ); \
116 1009 : CHECK( wallclock_millis<WALLCLOCK_MAX_MILLIS ); \
117 1008 : (dst) = wallclock_millis; \
118 967 : } while( 0 )
119 :
120 : static int
121 : deser_vote_instruction( uchar const * data,
122 52 : ulong data_len ) {
123 52 : fd_vote_instruction_t vote_instruction[1];
124 52 : CHECK( fd_vote_instruction_deserialize( vote_instruction, data, data_len ) );
125 52 : CHECK(
126 52 : vote_instruction->discriminant==fd_vote_instruction_enum_vote ||
127 52 : vote_instruction->discriminant==fd_vote_instruction_enum_vote_switch ||
128 52 : vote_instruction->discriminant==fd_vote_instruction_enum_update_vote_state ||
129 52 : vote_instruction->discriminant==fd_vote_instruction_enum_update_vote_state_switch ||
130 52 : vote_instruction->discriminant==fd_vote_instruction_enum_compact_update_vote_state ||
131 52 : vote_instruction->discriminant==fd_vote_instruction_enum_compact_update_vote_state_switch ||
132 52 : vote_instruction->discriminant==fd_vote_instruction_enum_tower_sync ||
133 52 : vote_instruction->discriminant==fd_vote_instruction_enum_tower_sync_switch );
134 : // Oddly, trailing garbage is allowed here at the end of the instruction
135 52 : return 1;
136 52 : }
137 :
138 : static int
139 : deser_vote_txn( fd_gossip_vote_t * vote,
140 : uchar const ** payload,
141 53 : ulong * payload_sz ) {
142 53 : uchar const * payload_start = *payload;
143 :
144 53 : ushort signatures_len;
145 53 : READ_U16_VARINT( signatures_len, payload, payload_sz );
146 53 : SKIP_BYTES( signatures_len*64UL, payload, payload_sz );
147 52 : uchar num_required_signatures, num_readonly_signed_accounts, num_readonly_unsigned_accounts;
148 52 : READ_U8( num_required_signatures, payload, payload_sz );
149 52 : READ_U8( num_readonly_signed_accounts, payload, payload_sz );
150 52 : READ_U8( num_readonly_unsigned_accounts, payload, payload_sz );
151 52 : ushort account_keys_len;
152 52 : READ_U16_VARINT( account_keys_len, payload, payload_sz );
153 52 : uchar const * account_keys = *payload;
154 52 : SKIP_BYTES( account_keys_len*32UL, payload, payload_sz );
155 52 : SKIP_BYTES( 32UL, payload, payload_sz ); /* recent blockhash */
156 52 : ushort instructions_len;
157 52 : READ_U16_VARINT( instructions_len, payload, payload_sz );
158 104 : for( ulong i=0UL; i<instructions_len; i++ ) {
159 52 : uchar program_id_index;
160 52 : READ_U8( program_id_index, payload, payload_sz );
161 52 : CHECK( program_id_index<account_keys_len );
162 52 : CHECK( program_id_index );
163 52 : ushort accounts_len;
164 52 : READ_U16_VARINT( accounts_len, payload, payload_sz );
165 163 : for( ulong j=0UL; j<accounts_len; j++ ) {
166 111 : uchar account_index;
167 111 : READ_U8( account_index, payload, payload_sz );
168 111 : CHECK( account_index<account_keys_len );
169 111 : }
170 52 : ushort data_len;
171 52 : READ_U16_VARINT( data_len, payload, payload_sz );
172 52 : uchar data[ 1232UL ];
173 52 : READ_BYTES( data, data_len, payload, payload_sz );
174 52 : if( FD_LIKELY( i==0UL ) ) {
175 52 : CHECK( accounts_len );
176 52 : uchar const * account_key = account_keys+32UL*program_id_index;
177 52 : CHECK( !memcmp( account_key, fd_solana_vote_program_id.uc, 32UL ) );
178 52 : CHECK( deser_vote_instruction( data, data_len ) );
179 52 : }
180 52 : }
181 :
182 52 : CHECK( num_required_signatures<=signatures_len );
183 52 : CHECK( signatures_len<=account_keys_len );
184 52 : CHECK( num_required_signatures+num_readonly_unsigned_accounts<=account_keys_len );
185 52 : CHECK( num_readonly_signed_accounts<num_required_signatures );
186 52 : CHECK( instructions_len );
187 :
188 52 : vote->transaction_len = (ulong)(*payload-payload_start);
189 52 : fd_memcpy( vote->transaction, payload_start, vote->transaction_len );
190 52 : return 1;
191 52 : }
192 :
193 : static int
194 : deser_vote( fd_gossip_value_t * value,
195 : uchar const ** payload,
196 75 : ulong * payload_sz ) {
197 75 : READ_U8( value->vote->index, payload, payload_sz );
198 75 : CHECK( value->vote->index<FD_GOSSIP_VOTE_IDX_MAX );
199 53 : READ_BYTES( value->origin, 32UL, payload, payload_sz );
200 :
201 53 : CHECK( deser_vote_txn( value->vote, payload, payload_sz ) );
202 52 : READ_WALLCLOCK( value->wallclock, payload, payload_sz );
203 52 : return 1;
204 52 : }
205 :
206 : static int
207 : deser_lowest_slot( fd_gossip_value_t * value,
208 : uchar const ** payload,
209 94 : ulong * payload_sz ) {
210 94 : uchar ix;
211 94 : READ_U8( ix, payload, payload_sz );
212 94 : CHECK( !ix );
213 63 : READ_BYTES( value->origin, 32UL, payload, payload_sz );
214 63 : ulong root;
215 63 : READ_U64( root, payload, payload_sz );
216 63 : CHECK( !root );
217 63 : READ_U64( value->lowest_slot->lowest, payload, payload_sz );
218 63 : CHECK( value->lowest_slot->lowest<MAX_SLOT );
219 63 : ulong slots_len;
220 63 : READ_U64( slots_len, payload, payload_sz );
221 63 : CHECK( !slots_len );
222 63 : ulong stash_len;
223 63 : READ_U64( stash_len, payload, payload_sz );
224 63 : CHECK( !stash_len );
225 63 : READ_WALLCLOCK( value->wallclock, payload, payload_sz );
226 63 : return 1;
227 63 : }
228 :
229 : static int
230 : deser_bitvec_u8_epoch_slots( uchar const ** payload,
231 46 : ulong * payload_sz ) {
232 46 : uchar has_bits;
233 46 : READ_OPTION( has_bits, payload, payload_sz );
234 46 : if( FD_UNLIKELY( !has_bits ) ) {
235 16 : ulong bits_cnt;
236 16 : READ_U64( bits_cnt, payload, payload_sz );
237 16 : CHECK( !bits_cnt );
238 15 : return 1;
239 16 : }
240 :
241 30 : ulong bits_cap;
242 30 : READ_U64( bits_cap, payload, payload_sz );
243 30 : SKIP_BYTES( bits_cap, payload, payload_sz );
244 30 : ulong bits_cnt;
245 30 : READ_U64( bits_cnt, payload, payload_sz );
246 30 : CHECK( bits_cnt==bits_cap*8UL );
247 27 : return 1;
248 30 : }
249 :
250 : static int
251 : deser_epoch_slots( fd_gossip_value_t * value,
252 : uchar const ** payload,
253 79 : ulong * payload_sz ) {
254 79 : READ_U8( value->epoch_slots->index, payload, payload_sz );
255 79 : CHECK( value->epoch_slots->index<FD_GOSSIP_EPOCH_SLOTS_IDX_MAX );
256 79 : READ_BYTES( value->origin, 32UL, payload, payload_sz );
257 79 : ulong slots_len;
258 79 : READ_U64( slots_len, payload, payload_sz );
259 154 : for( ulong i=0UL; i<slots_len; i++ ) {
260 103 : uint is_uncompressed;
261 103 : READ_ENUM( is_uncompressed, 2UL, payload, payload_sz );
262 79 : ulong first_slot;
263 79 : READ_U64( first_slot, payload, payload_sz );
264 79 : CHECK( first_slot<MAX_SLOT );
265 79 : ulong num;
266 79 : READ_U64( num, payload, payload_sz );
267 79 : CHECK( num<MAX_SLOTS_PER_EPOCH_SLOT );
268 79 : if( FD_UNLIKELY( is_uncompressed ) ) {
269 47 : CHECK( deser_bitvec_u8_epoch_slots( payload, payload_sz ) );
270 47 : } else {
271 32 : ulong compressed_len;
272 32 : READ_U64( compressed_len, payload, payload_sz );
273 32 : SKIP_BYTES( compressed_len, payload, payload_sz );
274 32 : }
275 79 : }
276 51 : READ_WALLCLOCK( value->wallclock, payload, payload_sz );
277 51 : return 1;
278 51 : }
279 :
280 : static int
281 : deser_duplicate_shred( fd_gossip_value_t * value,
282 : uchar const ** payload,
283 74 : ulong * payload_sz ) {
284 74 : READ_U16( value->duplicate_shred->index, payload, payload_sz );
285 74 : CHECK( value->duplicate_shred->index<FD_GOSSIP_DUPLICATE_SHRED_IDX_MAX );
286 46 : READ_BYTES( value->origin, 32UL, payload, payload_sz );
287 46 : READ_WALLCLOCK( value->wallclock, payload, payload_sz );
288 46 : READ_U64( value->duplicate_shred->slot, payload, payload_sz );
289 46 : SKIP_BYTES( 5UL, payload, payload_sz ); /* (unused) + shred type (unused) */
290 46 : READ_U8( value->duplicate_shred->num_chunks, payload, payload_sz );
291 46 : READ_U8( value->duplicate_shred->chunk_index, payload, payload_sz );
292 46 : CHECK( value->duplicate_shred->chunk_index<value->duplicate_shred->num_chunks );
293 46 : READ_U64( value->duplicate_shred->chunk_len, payload, payload_sz );
294 46 : READ_BYTES( value->duplicate_shred->chunk, value->duplicate_shred->chunk_len, payload, payload_sz );
295 46 : return 1;
296 46 : }
297 :
298 : static int
299 : deser_snapshot_hashes( fd_gossip_value_t * value,
300 : uchar const ** payload,
301 86 : ulong * payload_sz ) {
302 86 : READ_BYTES( value->origin, 32UL, payload, payload_sz );
303 86 : READ_U64( value->snapshot_hashes->full_slot, payload, payload_sz );
304 86 : CHECK( value->snapshot_hashes->full_slot<MAX_SLOT );
305 61 : READ_BYTES( value->snapshot_hashes->full_hash, 32UL, payload, payload_sz );
306 61 : READ_U64( value->snapshot_hashes->incremental_len, payload, payload_sz );
307 150 : for( ulong i=0UL; i<value->snapshot_hashes->incremental_len; i++ ) {
308 89 : READ_U64( value->snapshot_hashes->incremental[ i ].slot, payload, payload_sz );
309 89 : CHECK( value->snapshot_hashes->incremental[ i ].slot<MAX_SLOT );
310 89 : CHECK( value->snapshot_hashes->incremental[ i ].slot>value->snapshot_hashes->full_slot );
311 89 : READ_BYTES( value->snapshot_hashes->incremental[ i ].hash, 32UL, payload, payload_sz );
312 89 : }
313 61 : READ_WALLCLOCK( value->wallclock, payload, payload_sz );
314 61 : return 1;
315 61 : }
316 :
317 : static int
318 : deser_contact_info( fd_gossip_value_t * value,
319 : uchar const ** payload,
320 84 : ulong * payload_sz ) {
321 84 : READ_BYTES( value->origin, 32UL, payload, payload_sz );
322 84 : READ_U64_VARINT( value->wallclock, payload, payload_sz );
323 84 : CHECK( value->wallclock<WALLCLOCK_MAX_MILLIS );
324 84 : READ_U64( value->contact_info->outset, payload, payload_sz );
325 84 : READ_U16( value->contact_info->shred_version, payload, payload_sz );
326 84 : READ_U16_VARINT( value->contact_info->version.major, payload, payload_sz );
327 78 : READ_U16_VARINT( value->contact_info->version.minor, payload, payload_sz );
328 75 : READ_U16_VARINT( value->contact_info->version.patch, payload, payload_sz );
329 70 : READ_U32( value->contact_info->version.commit, payload, payload_sz );
330 70 : READ_U32( value->contact_info->version.feature_set, payload, payload_sz );
331 70 : READ_U16_VARINT( value->contact_info->version.client, payload, payload_sz );
332 :
333 : /* Tightest bounds for array sizes given network constraints.
334 :
335 : IPv6 minimum MTU = 1280
336 : IPv6 header = 40
337 : UDP header = 8
338 : PACKET_DATA_SIZE = 1232 (= 1280 - 40 - 8)
339 :
340 : Bytes consumed for preamble:
341 : Push/Pull Response:
342 : Protocol tag(4) + from(32) + values_len(8) = 44
343 : Pull Request:
344 : Protocol tag(4) + keys_len(8) + bloom_none(9) +
345 : num_bits_set(8) + mask(8) + mask_bits(4) = 41
346 : Minimum consumed for preamble: 41
347 :
348 : Bytes consumed before addrs loop:
349 : Preamble(41) + signature(64) +
350 : CrdsData tag(4) + origin(32) + wallclock_varint(1) + outset(8) +
351 : shred_version(2) + major(1) + minor(1) + patch(1) + commit(4) +
352 : feature_set(4) + client(1) + addrs_len_varint(1) = 165
353 :
354 : Remaining: 1232 - 165 = 1067
355 : Each addr: READ_ENUM(4) + READ_U32(4) = 8 bytes minimum
356 : Max addrs = floor(1067/8) = 133
357 :
358 : Bytes consumed before sockets loop:
359 : (same as above) + sockets_len_varint(1) = 166
360 :
361 : Remaining: 1232 - 166 = 1066
362 : Each socket: READ_U8(1) + READ_U8(1) + READ_U16_VARINT(1) = 3 bytes minimum
363 : Max sockets = floor(1066/3) = 355 */
364 :
365 67 : #define FD_GOSSIP_CONTACT_INFO_MAX_ADDRESSES (133UL)
366 67 : #define FD_GOSSIP_CONTACT_INFO_MAX_SOCKETS (355UL)
367 :
368 67 : uint is_ip6[ FD_GOSSIP_CONTACT_INFO_MAX_ADDRESSES ];
369 67 : union {
370 67 : uint ip4;
371 67 : uchar ip6[ 16UL ];
372 67 : } ips[ FD_GOSSIP_CONTACT_INFO_MAX_ADDRESSES ];
373 :
374 67 : ulong addrs_len;
375 67 : READ_U16_VARINT( addrs_len, payload, payload_sz );
376 204 : for( ulong i=0UL; i<addrs_len; i++ ) {
377 147 : READ_ENUM( is_ip6[ i ], 2UL, payload, payload_sz );
378 137 : if( !is_ip6[ i ] ) READ_U32( ips[ i ].ip4, payload, payload_sz );
379 49 : else READ_BYTES( ips[ i ].ip6, 16UL, payload, payload_sz );
380 137 : }
381 :
382 57 : struct {
383 57 : uchar key;
384 57 : uchar index;
385 57 : ushort offset;
386 57 : } sockets[ FD_GOSSIP_CONTACT_INFO_MAX_SOCKETS ];
387 :
388 57 : ulong sockets_len;
389 57 : READ_U16_VARINT( sockets_len, payload, payload_sz );
390 194 : for( ulong i=0UL; i<sockets_len; i++ ) {
391 137 : READ_U8( sockets[ i ].key, payload, payload_sz );
392 137 : READ_U8( sockets[ i ].index, payload, payload_sz );
393 137 : READ_U16_VARINT( sockets[ i ].offset, payload, payload_sz );
394 137 : }
395 :
396 57 : ulong extensions_len;
397 57 : READ_U16_VARINT( extensions_len, payload, payload_sz );
398 57 : for( ulong i=0UL; i<extensions_len; i++ ) {
399 0 : SKIP_BYTES( 1UL, payload, payload_sz ); /* type */
400 0 : ushort bytes_len;
401 0 : READ_U16_VARINT( bytes_len, payload, payload_sz );
402 0 : SKIP_BYTES( bytes_len, payload, payload_sz );
403 0 : }
404 :
405 : /* Ipv6 and duplicate IPs are not allowed
406 : https://github.com/anza-xyz/agave/blob/v4.2.0-beta.0/gossip/src/contact_info.rs#L667-L676 */
407 127 : for( ulong i=0UL; i<addrs_len; i++ ) {
408 103 : CHECK( !is_ip6[ i ] );
409 113 : for( ulong j=0UL; j<i; j++ ) {
410 43 : CHECK( ips[ i ].ip4!=ips[ j ].ip4 );
411 43 : }
412 70 : }
413 :
414 : /* Each socket must reference unique key */
415 24 : int seen_socket_key[ 256UL ] = {0};
416 68 : for( ulong i=0UL; i<sockets_len; i++ ) {
417 44 : CHECK( !seen_socket_key[ sockets[ i ].key ] );
418 44 : seen_socket_key[ sockets[ i ].key ] = 1;
419 44 : }
420 :
421 : /* Each IP address must be referenced by at least one socket */
422 24 : int seen_ip_addr[ FD_GOSSIP_CONTACT_INFO_MAX_ADDRESSES ] = {0};
423 68 : for( ulong i=0UL; i<sockets_len; i++ ) {
424 44 : CHECK( sockets[ i ].index<addrs_len );
425 44 : seen_ip_addr[ sockets[ i ].index ] = 1;
426 44 : }
427 68 : for( ulong i=0UL; i<addrs_len; i++ ) CHECK( seen_ip_addr[ i ] );
428 :
429 : /* Port offsets don't overflow */
430 24 : ushort cur_port = 0U;
431 68 : for( ulong i=0UL; i<sockets_len; i++ ) {
432 44 : ushort result;
433 44 : CHECK( !__builtin_add_overflow( cur_port, sockets[ i ].offset, &result ) );
434 44 : cur_port = result;
435 44 : }
436 :
437 24 : memset( value->contact_info->sockets, 0, sizeof( value->contact_info->sockets ) );
438 :
439 24 : cur_port = 0U;
440 68 : for( ulong i=0UL; i<sockets_len; i++ ) {
441 44 : cur_port = (ushort)(cur_port + sockets[ i ].offset);
442 44 : if( FD_LIKELY( sockets[ i ].key<FD_GOSSIP_CONTACT_INFO_SOCKET_CNT ) ) {
443 44 : value->contact_info->sockets[ sockets[ i ].key ].is_ipv6 = is_ip6[ sockets[ i ].index ];
444 44 : if( FD_LIKELY( !is_ip6[ sockets[ i ].index ] ) ) value->contact_info->sockets[ sockets[ i ].key ].ip4 = ips[ sockets[ i ].index ].ip4;
445 0 : else fd_memcpy( value->contact_info->sockets[ sockets[ i ].key ].ip6, ips[ sockets[ i ].index ].ip6, 16UL );
446 :
447 44 : value->contact_info->sockets[ sockets[ i ].key ].port = fd_ushort_bswap( cur_port );
448 44 : }
449 44 : }
450 24 : return 1;
451 24 : }
452 :
453 : /* https://github.com/anza-xyz/wincode/blob/wincode%40v0.6.1/wincode/src/schema/external/bv.rs#L89-L147 */
454 : static int
455 : deser_bitvec_u8_restart_last_voted_fork_slots( uchar const ** payload,
456 25 : ulong * payload_sz ) {
457 25 : uchar has_bits;
458 25 : READ_OPTION( has_bits, payload, payload_sz );
459 25 : if( FD_UNLIKELY( !has_bits ) ) {
460 12 : ulong bits_len;
461 12 : READ_U64( bits_len, payload, payload_sz );
462 12 : CHECK( !bits_len );
463 12 : return 1;
464 12 : }
465 :
466 13 : ulong bits_cap;
467 13 : READ_U64( bits_cap, payload, payload_sz );
468 13 : SKIP_BYTES( bits_cap, payload, payload_sz );
469 13 : ulong bits_len;
470 13 : READ_U64( bits_len, payload, payload_sz );
471 13 : CHECK( bits_len<=bits_cap*8UL );
472 13 : return 1;
473 13 : }
474 :
475 : static int
476 : deser_restart_last_voted_fork_slots( fd_gossip_value_t * value,
477 : uchar const ** payload,
478 72 : ulong * payload_sz ) {
479 72 : READ_BYTES( value->origin, 32UL, payload, payload_sz );
480 72 : READ_WALLCLOCK( value->wallclock, payload, payload_sz );
481 48 : uint is_raw_offsets;
482 48 : READ_ENUM( is_raw_offsets, 2UL, payload, payload_sz );
483 48 : if( FD_LIKELY( is_raw_offsets ) ) {
484 25 : CHECK( deser_bitvec_u8_restart_last_voted_fork_slots( payload, payload_sz ) );
485 25 : } else {
486 23 : ulong slots_len;
487 23 : READ_U64( slots_len, payload, payload_sz );
488 125 : for( ulong i=0UL; i<slots_len; i++ ) {
489 102 : ushort _slot;
490 102 : READ_U16_VARINT( _slot, payload, payload_sz );
491 102 : (void)_slot;
492 102 : }
493 23 : }
494 48 : SKIP_BYTES( 8UL+32UL+2UL, payload, payload_sz ); /* last voted slot + last voted hash + shred version */
495 48 : return 1;
496 48 : }
497 :
498 : static int
499 : deser_restart_heaviest_fork( fd_gossip_value_t * value,
500 : uchar const ** payload,
501 65 : ulong * payload_sz ) {
502 65 : READ_BYTES( value->origin, 32UL, payload, payload_sz );
503 65 : READ_WALLCLOCK( value->wallclock, payload, payload_sz );
504 47 : SKIP_BYTES( 8UL+32UL+8UL+2UL, payload, payload_sz ); /* last slot + last slot hash + observed stake + shred version */
505 47 : return 1;
506 47 : }
507 :
508 : static int
509 : deser_value( fd_gossip_value_t * value,
510 : uchar const ** payload,
511 1650 : ulong * payload_sz ) {
512 1650 : READ_BYTES( value->signature, 64UL, payload, payload_sz );
513 1632 : READ_ENUM( value->tag, FD_GOSSIP_VALUE_CNT, payload, payload_sz );
514 :
515 1160 : switch( value->tag ) {
516 75 : case FD_GOSSIP_VALUE_LEGACY_CONTACT_INFO: return 0; /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/gossip/src/legacy_contact_info.rs#L41 */
517 75 : case FD_GOSSIP_VALUE_VOTE: return deser_vote( value, payload, payload_sz );
518 94 : case FD_GOSSIP_VALUE_LOWEST_SLOT: return deser_lowest_slot( value, payload, payload_sz );
519 86 : case FD_GOSSIP_VALUE_LEGACY_SNAPSHOT_HASHES: return 0; /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/gossip/src/crds_data.rs#L224 */
520 83 : case FD_GOSSIP_VALUE_ACCOUNT_HASHES: return 0; /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/gossip/src/crds_data.rs#L224 */
521 79 : case FD_GOSSIP_VALUE_EPOCH_SLOTS: return deser_epoch_slots( value, payload, payload_sz );
522 105 : case FD_GOSSIP_VALUE_LEGACY_VERSION: return 0; /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/gossip/src/crds_data.rs#L431 */
523 92 : case FD_GOSSIP_VALUE_VERSION: return 0; /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/gossip/src/crds_data.rs#L448 */
524 91 : case FD_GOSSIP_VALUE_NODE_INSTANCE: return 0; /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/gossip/src/crds_data.rs#L466 */
525 74 : case FD_GOSSIP_VALUE_DUPLICATE_SHRED: return deser_duplicate_shred( value, payload, payload_sz );
526 86 : case FD_GOSSIP_VALUE_SNAPSHOT_HASHES: return deser_snapshot_hashes( value, payload, payload_sz );
527 84 : case FD_GOSSIP_VALUE_CONTACT_INFO: return deser_contact_info( value, payload, payload_sz );
528 72 : case FD_GOSSIP_VALUE_RESTART_LAST_VOTED_FORK_SLOTS: return deser_restart_last_voted_fork_slots( value, payload, payload_sz );
529 65 : case FD_GOSSIP_VALUE_RESTART_HEAVIEST_FORK: return deser_restart_heaviest_fork( value, payload, payload_sz );
530 0 : default: FD_LOG_CRIT(( "impossible" ));
531 1160 : }
532 1160 : }
533 :
534 : /* https://github.com/anza-xyz/wincode/blob/wincode%40v0.6.1/wincode/src/schema/external/bv.rs#L89-L147 */
535 : static int
536 : deser_bitvec_u64( fd_gossip_bloom_t * bloom,
537 : uchar const ** payload,
538 562 : ulong * payload_sz ) {
539 562 : uchar has_bits;
540 562 : READ_OPTION( has_bits, payload, payload_sz );
541 180 : if( FD_UNLIKELY( !has_bits ) ) {
542 2 : bloom->bits_cap = 0UL;
543 2 : READ_U64( bloom->bits_len, payload, payload_sz );
544 2 : CHECK( !bloom->bits_len );
545 1 : return 1;
546 2 : }
547 :
548 178 : READ_U64( bloom->bits_cap, payload, payload_sz );
549 178 : ulong bits_sz;
550 178 : CHECK( !__builtin_mul_overflow( bloom->bits_cap, 8UL, &bits_sz ) );
551 33 : READ_BYTES( bloom->bits, bits_sz, payload, payload_sz );
552 11 : READ_U64( bloom->bits_len, payload, payload_sz );
553 11 : CHECK( bloom->bits_len<=bits_sz*8UL );
554 6 : return 1;
555 11 : }
556 :
557 : static int
558 : deser_pull_request( fd_gossip_message_t * message,
559 : uchar const ** payload,
560 : ulong * payload_sz,
561 818 : ulong original_sz ) {
562 818 : READ_U64( message->pull_request->crds_filter->filter->keys_len, payload, payload_sz );
563 22331 : for( ulong i=0UL; i<message->pull_request->crds_filter->filter->keys_len; i++ ) {
564 21769 : READ_U64( message->pull_request->crds_filter->filter->keys[ i ], payload, payload_sz );
565 21769 : }
566 :
567 562 : CHECK( deser_bitvec_u64( message->pull_request->crds_filter->filter, payload, payload_sz ) );
568 :
569 7 : READ_U64( message->pull_request->crds_filter->filter->num_bits_set, payload, payload_sz );
570 7 : READ_U64( message->pull_request->crds_filter->mask, payload, payload_sz );
571 7 : READ_U32( message->pull_request->crds_filter->mask_bits, payload, payload_sz );
572 :
573 : /* https://github.com/anza-xyz/agave/blob/v4.2.0-beta.0/gossip/src/crds_gossip_pull.rs#L101 */
574 7 : CHECK( message->pull_request->crds_filter->mask_bits>=FD_GOSSIP_MIN_PULL_REQUEST_MASK_BITS );
575 :
576 7 : message->pull_request->contact_info->offset = original_sz-*payload_sz;
577 7 : CHECK( deser_value( message->pull_request->contact_info, payload, payload_sz ) );
578 7 : message->pull_request->contact_info->length = original_sz-*payload_sz-message->pull_request->contact_info->offset;
579 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/gossip/src/protocol.rs#L158 */
580 7 : CHECK( message->pull_request->contact_info->tag==FD_GOSSIP_VALUE_CONTACT_INFO );
581 7 : return 1;
582 7 : }
583 :
584 : static int
585 : deser_pull_response( fd_gossip_message_t * message,
586 : uchar const ** payload,
587 : ulong * payload_sz,
588 822 : ulong original_sz ) {
589 822 : READ_BYTES( message->pull_response->from, 32UL, payload, payload_sz );
590 822 : READ_U64( message->pull_response->values_len, payload, payload_sz );
591 1014 : for( ulong i=0UL; i<message->pull_response->values_len; i++ ) {
592 822 : message->pull_response->values[ i ].offset = original_sz-*payload_sz;
593 822 : CHECK( deser_value( &message->pull_response->values[ i ], payload, payload_sz ) );
594 192 : message->pull_response->values[ i ].length = original_sz-*payload_sz-message->pull_response->values[ i ].offset;
595 192 : }
596 192 : return 1;
597 822 : }
598 :
599 : static int
600 : deser_push( fd_gossip_message_t * message,
601 : uchar const ** payload,
602 : ulong * payload_sz,
603 823 : ulong original_sz ) {
604 823 : READ_BYTES( message->push->from, 32UL, payload, payload_sz );
605 823 : READ_U64( message->push->values_len, payload, payload_sz );
606 1017 : for( ulong i=0UL; i<message->push->values_len; i++ ) {
607 823 : message->push->values[ i ].offset = original_sz-*payload_sz;
608 823 : CHECK( deser_value( &message->push->values[ i ], payload, payload_sz ) );
609 194 : message->push->values[ i ].length = original_sz-*payload_sz-message->push->values[ i ].offset;
610 194 : }
611 194 : return 1;
612 823 : }
613 :
614 : static int
615 : deser_prune( fd_gossip_message_t * message,
616 : uchar const ** payload,
617 846 : ulong * payload_sz ) {
618 846 : READ_BYTES( message->prune->sender, 32UL, payload, payload_sz );
619 846 : READ_BYTES( message->prune->pubkey, 32UL, payload, payload_sz );
620 846 : CHECK( !memcmp( message->prune->sender, message->prune->pubkey, 32UL ) );
621 599 : READ_U64( message->prune->prunes_len, payload, payload_sz );
622 9163 : for( ulong i=0UL; i<message->prune->prunes_len; i++ ) {
623 8564 : READ_BYTES( message->prune->prunes[ i ], 32UL, payload, payload_sz );
624 8564 : }
625 599 : READ_BYTES( message->prune->signature, 64UL, payload, payload_sz );
626 599 : READ_BYTES( message->prune->destination, 32UL, payload, payload_sz );
627 599 : READ_WALLCLOCK( message->prune->wallclock, payload, payload_sz );
628 599 : return 1;
629 599 : }
630 :
631 : static int
632 : deser_ping( fd_gossip_message_t * message,
633 : uchar const ** payload,
634 850 : ulong * payload_sz ) {
635 850 : READ_BYTES( message->ping->from, 32UL, payload, payload_sz );
636 850 : READ_BYTES( message->ping->token, 32UL, payload, payload_sz );
637 850 : READ_BYTES( message->ping->signature, 64UL, payload, payload_sz );
638 850 : return 1;
639 850 : }
640 :
641 : static int
642 : deser_pong( fd_gossip_message_t * message,
643 : uchar const ** payload,
644 857 : ulong * payload_sz ) {
645 857 : READ_BYTES( message->pong->from, 32UL, payload, payload_sz );
646 857 : READ_BYTES( message->pong->hash, 32UL, payload, payload_sz );
647 857 : READ_BYTES( message->pong->signature, 64UL, payload, payload_sz );
648 857 : return 1;
649 857 : }
650 :
651 : int
652 : fd_gossip_message_deserialize( fd_gossip_message_t * message,
653 : uchar const * _payload,
654 5016 : ulong _payload_sz ) {
655 5016 : uchar const ** payload = &_payload;
656 5016 : ulong * payload_sz = &_payload_sz;
657 5016 : ulong original_sz = _payload_sz;
658 :
659 5016 : CHECK( _payload_sz<=1232UL );
660 5016 : READ_ENUM( message->tag, FD_GOSSIP_MESSAGE_CNT, payload, payload_sz );
661 :
662 5016 : switch( message->tag ){
663 818 : case FD_GOSSIP_MESSAGE_PULL_REQUEST: CHECK( deser_pull_request( message, payload, payload_sz, original_sz ) ); break;
664 822 : case FD_GOSSIP_MESSAGE_PULL_RESPONSE: CHECK( deser_pull_response( message, payload, payload_sz, original_sz ) ); break;
665 823 : case FD_GOSSIP_MESSAGE_PUSH: CHECK( deser_push( message, payload, payload_sz, original_sz ) ); break;
666 846 : case FD_GOSSIP_MESSAGE_PRUNE: CHECK( deser_prune( message, payload, payload_sz ) ); break;
667 850 : case FD_GOSSIP_MESSAGE_PING: CHECK( deser_ping( message, payload, payload_sz ) ); break;
668 857 : case FD_GOSSIP_MESSAGE_PONG: CHECK( deser_pong( message, payload, payload_sz ) ); break;
669 857 : default: FD_LOG_CRIT(( "invalid message tag" ));
670 5016 : }
671 :
672 2690 : return !*payload_sz;
673 5016 : }
674 :
675 0 : #define CHECK1( cond ) do { \
676 0 : if( FD_UNLIKELY( !(cond) ) ) return -1; \
677 0 : } while( 0 )
678 :
679 0 : #define WRITE_BYTES( src, src_sz, out, out_sz ) do { \
680 0 : CHECK1( *out_sz>=src_sz ); \
681 0 : fd_memcpy( *out, src, src_sz ); \
682 0 : (*out) += src_sz; \
683 0 : (*out_sz) -= src_sz; \
684 0 : } while( 0 )
685 :
686 0 : #define WRITE_SKIP_BYTES( skip_sz, out, out_sz ) do { \
687 0 : CHECK1( *out_sz>=skip_sz ); \
688 0 : (*out) += skip_sz; \
689 0 : (*out_sz) -= skip_sz; \
690 0 : } while( 0 )
691 :
692 0 : #define WRITE_U8( val, out, out_sz ) do { \
693 0 : CHECK1( *out_sz>=1UL ); \
694 0 : FD_STORE( uchar, *out, val ); \
695 0 : (*out) += 1UL; \
696 0 : (*out_sz) -= 1UL; \
697 0 : } while( 0 )
698 :
699 0 : #define WRITE_U16( val, out, out_sz ) do { \
700 0 : CHECK1( *out_sz>=2UL ); \
701 0 : FD_STORE( ushort, *out, val ); \
702 0 : (*out) += 2UL; \
703 0 : (*out_sz) -= 2UL; \
704 0 : } while( 0 )
705 :
706 0 : #define WRITE_U32( val, out, out_sz ) do { \
707 0 : CHECK1( *out_sz>=4UL ); \
708 0 : FD_STORE( uint, *out, val ); \
709 0 : (*out) += 4UL; \
710 0 : (*out_sz) -= 4UL; \
711 0 : } while( 0 )
712 :
713 0 : #define WRITE_U64( val, out, out_sz ) do { \
714 0 : CHECK1( *out_sz>=8UL ); \
715 0 : FD_STORE( ulong, *out, val ); \
716 0 : (*out) += 8UL; \
717 0 : (*out_sz) -= 8UL; \
718 0 : } while( 0 )
719 :
720 0 : #define WRITE_U16_VARINT( val, out, out_sz ) do { \
721 0 : ushort _val = (val); \
722 0 : if( FD_LIKELY( _val<128U ) ) { \
723 0 : CHECK1( *(out_sz)>=1UL ); \
724 0 : FD_STORE( uchar, *out, (uchar)_val ); \
725 0 : (*out) += 1UL; \
726 0 : (*out_sz) -= 1UL; \
727 0 : } else if( FD_LIKELY( _val<16384U ) ) { \
728 0 : CHECK1( *out_sz>=2UL ); \
729 0 : FD_STORE( uchar, (*out), (uchar)((_val&0x7FU)|0x80U) ); \
730 0 : FD_STORE( uchar, (*out)+1, (uchar)(_val>>7U) ); \
731 0 : (*out) += 2UL; \
732 0 : (*out_sz) -= 2UL; \
733 0 : } else { \
734 0 : CHECK1( *out_sz>=3UL ); \
735 0 : FD_STORE( uchar, (*out), (uchar)((_val&0x7FU)|0x80U) ); \
736 0 : FD_STORE( uchar, (*out)+1, (uchar)(((_val>>7U)&0x7FU)|0x80U) ); \
737 0 : FD_STORE( uchar, (*out)+2, (uchar)(_val>>14U) ); \
738 0 : (*out) += 3UL; \
739 0 : (*out_sz) -= 3UL; \
740 0 : } \
741 0 : } while( 0 )
742 :
743 0 : #define WRITE_U64_VARINT( val, out, out_sz ) do { \
744 0 : ulong _val = (val); \
745 0 : while( _val>=0x80UL ) { \
746 0 : CHECK1( *(out_sz)>=1UL ); \
747 0 : FD_STORE( uchar, *out, (uchar)((_val&0x7FUL)|0x80UL) ); \
748 0 : (*out) += 1UL; \
749 0 : (*out_sz) -= 1UL; \
750 0 : _val >>= 7; \
751 0 : } \
752 0 : CHECK1( *(out_sz)>=1UL ); \
753 0 : FD_STORE( uchar, *out, (uchar)_val ); \
754 0 : (*out) += 1UL; \
755 0 : (*out_sz) -= 1UL; \
756 0 : } while( 0 )
757 :
758 : static int
759 : ser_vote( fd_gossip_value_t const * value,
760 : uchar ** out,
761 0 : ulong * out_sz ) {
762 0 : WRITE_U8( value->vote->index, out, out_sz );
763 0 : WRITE_BYTES( value->origin, 32UL, out, out_sz );
764 0 : WRITE_BYTES( value->vote->transaction, value->vote->transaction_len, out, out_sz );
765 0 : WRITE_U64( value->wallclock, out, out_sz );
766 0 : return 1;
767 0 : }
768 :
769 : static int
770 : ser_duplicate_shred( fd_gossip_value_t const * value,
771 : uchar ** out,
772 0 : ulong * out_sz ) {
773 0 : WRITE_U16( value->duplicate_shred->index, out, out_sz );
774 0 : WRITE_BYTES( value->origin, 32UL, out, out_sz );
775 0 : WRITE_U64( value->wallclock, out, out_sz );
776 0 : WRITE_U64( value->duplicate_shred->slot, out, out_sz );
777 0 : WRITE_BYTES( "\0\0\0\0\0", 5UL, out, out_sz ); /* (unused) + shred type (unused) */
778 0 : WRITE_U8( value->duplicate_shred->num_chunks, out, out_sz );
779 0 : WRITE_U8( value->duplicate_shred->chunk_index, out, out_sz );
780 0 : WRITE_U64( value->duplicate_shred->chunk_len, out, out_sz );
781 0 : WRITE_BYTES( value->duplicate_shred->chunk, value->duplicate_shred->chunk_len, out, out_sz );
782 0 : return 1;
783 0 : }
784 :
785 : static int
786 : ser_snapshot_hashes( fd_gossip_value_t const * value,
787 : uchar ** out,
788 0 : ulong * out_sz ) {
789 0 : WRITE_BYTES( value->origin, 32UL, out, out_sz );
790 0 : WRITE_U64( value->snapshot_hashes->full_slot, out, out_sz );
791 0 : WRITE_BYTES( value->snapshot_hashes->full_hash, 32UL, out, out_sz );
792 0 : WRITE_U64( value->snapshot_hashes->incremental_len, out, out_sz );
793 0 : for( ulong i=0UL; i<value->snapshot_hashes->incremental_len; i++ ) {
794 0 : WRITE_U64( value->snapshot_hashes->incremental[ i ].slot, out, out_sz );
795 0 : WRITE_BYTES( value->snapshot_hashes->incremental[ i ].hash, 32UL, out, out_sz );
796 0 : }
797 0 : WRITE_U64( value->wallclock, out, out_sz );
798 0 : return 1;
799 0 : }
800 :
801 : static int
802 : ser_contact_info( fd_gossip_value_t const * value,
803 : uchar ** out,
804 0 : ulong * out_sz ) {
805 0 : WRITE_BYTES( value->origin, 32UL, out, out_sz );
806 0 : WRITE_U64_VARINT( value->wallclock, out, out_sz );
807 0 : WRITE_U64( value->contact_info->outset, out, out_sz );
808 0 : WRITE_U16( value->contact_info->shred_version, out, out_sz );
809 0 : WRITE_U16_VARINT( value->contact_info->version.major, out, out_sz );
810 0 : WRITE_U16_VARINT( value->contact_info->version.minor, out, out_sz );
811 0 : WRITE_U16_VARINT( value->contact_info->version.patch, out, out_sz );
812 0 : WRITE_U32( value->contact_info->version.commit, out, out_sz );
813 0 : WRITE_U32( value->contact_info->version.feature_set, out, out_sz );
814 0 : WRITE_U16_VARINT( value->contact_info->version.client, out, out_sz );
815 :
816 0 : ulong num_sockets = 0UL;
817 0 : ulong num_unique_addrs = 0UL;
818 0 : int duplicate[ FD_GOSSIP_CONTACT_INFO_SOCKET_CNT ] = {0};
819 0 : ulong address_map[ FD_GOSSIP_CONTACT_INFO_SOCKET_CNT ];
820 0 : for( ulong i=0UL; i<FD_GOSSIP_CONTACT_INFO_SOCKET_CNT; i++ ) {
821 0 : if( FD_UNLIKELY( !value->contact_info->sockets[ i ].port ) ) continue;
822 0 : num_sockets++;
823 :
824 0 : if( FD_UNLIKELY( duplicate[ i ] ) ) continue;
825 :
826 0 : address_map[ i ] = num_unique_addrs;
827 0 : num_unique_addrs++;
828 :
829 0 : for( ulong j=i+1UL; j<FD_GOSSIP_CONTACT_INFO_SOCKET_CNT; j++ ) {
830 0 : if( FD_UNLIKELY( value->contact_info->sockets[ i ].is_ipv6!=value->contact_info->sockets[ j ].is_ipv6 ) ) continue;
831 0 : if( FD_LIKELY( !value->contact_info->sockets[ i ].is_ipv6 ) ) {
832 0 : if( FD_LIKELY( value->contact_info->sockets[ i ].ip4!=value->contact_info->sockets[ j ].ip4 ) ) continue;
833 0 : } else {
834 0 : if( FD_LIKELY( memcmp( value->contact_info->sockets[ i ].ip6, value->contact_info->sockets[ j ].ip6, 16UL ) ) ) continue;
835 0 : }
836 :
837 0 : duplicate[ j ] = 1;
838 0 : address_map[ j ] = address_map[ i ];
839 0 : }
840 0 : }
841 :
842 0 : WRITE_U16_VARINT( (ushort)num_unique_addrs, out, out_sz );
843 0 : for( ulong i=0UL; i<FD_GOSSIP_CONTACT_INFO_SOCKET_CNT; i++ ) {
844 0 : if( FD_UNLIKELY( !value->contact_info->sockets[ i ].port ) ) continue;
845 0 : if( FD_UNLIKELY( duplicate[ i ] ) ) continue;
846 :
847 0 : WRITE_U32( value->contact_info->sockets[ i ].is_ipv6, out, out_sz );
848 0 : if( FD_LIKELY( !value->contact_info->sockets[ i ].is_ipv6 ) ) WRITE_U32( value->contact_info->sockets[ i ].ip4, out, out_sz );
849 0 : else WRITE_BYTES( value->contact_info->sockets[ i ].ip6, 16UL, out, out_sz );
850 0 : }
851 :
852 0 : WRITE_U16_VARINT( (ushort)num_sockets, out, out_sz );
853 :
854 0 : int already_written[ FD_GOSSIP_CONTACT_INFO_SOCKET_CNT ] = {0};
855 0 : ushort prev_port = 0U;
856 0 : for( ulong i=0UL; i<num_sockets; i++ ) {
857 0 : ulong lowest_port_index = ULONG_MAX;
858 0 : for( ulong j=0UL; j<FD_GOSSIP_CONTACT_INFO_SOCKET_CNT; j++ ) {
859 0 : if( FD_UNLIKELY( !value->contact_info->sockets[ j ].port ) ) continue;
860 0 : if( FD_UNLIKELY( already_written[ j ] ) ) continue;
861 0 : if( FD_UNLIKELY( lowest_port_index==ULONG_MAX || fd_ushort_bswap( value->contact_info->sockets[ j ].port )<fd_ushort_bswap( value->contact_info->sockets[ lowest_port_index ].port ) ) ) lowest_port_index = j;
862 0 : }
863 0 : if( FD_UNLIKELY( lowest_port_index==ULONG_MAX ) ) break;
864 0 : already_written[ lowest_port_index ] = 1;
865 :
866 0 : WRITE_U8( (uchar)lowest_port_index, out, out_sz );
867 0 : WRITE_U8( (uchar)address_map[ lowest_port_index ], out, out_sz );
868 :
869 0 : ushort port_offset = (ushort)(fd_ushort_bswap( value->contact_info->sockets[ lowest_port_index ].port )-prev_port);
870 0 : WRITE_U16_VARINT( port_offset, out, out_sz );
871 0 : prev_port = fd_ushort_bswap( value->contact_info->sockets[ lowest_port_index ].port );
872 0 : }
873 :
874 0 : WRITE_U16_VARINT( 0UL, out, out_sz ); /* extensions_len */
875 0 : return 1;
876 0 : }
877 :
878 : long
879 : fd_gossip_value_serialize( fd_gossip_value_t const * value,
880 : uchar * _out,
881 0 : ulong _out_sz ) {
882 :
883 0 : uchar ** out = &_out;
884 0 : ulong original_size = _out_sz;
885 0 : ulong * out_sz = &_out_sz;
886 :
887 0 : WRITE_BYTES( value->signature, 64UL, out, out_sz );
888 0 : WRITE_U32( value->tag, out, out_sz );
889 :
890 0 : switch( value->tag ) {
891 0 : case FD_GOSSIP_VALUE_VOTE: if( FD_UNLIKELY( -1==ser_vote( value, out, out_sz ) ) ) return -1; break;
892 0 : case FD_GOSSIP_VALUE_DUPLICATE_SHRED: if( FD_UNLIKELY( -1==ser_duplicate_shred( value, out, out_sz ) ) ) return -1; break;
893 0 : case FD_GOSSIP_VALUE_SNAPSHOT_HASHES: if( FD_UNLIKELY( -1==ser_snapshot_hashes( value, out, out_sz ) ) ) return -1; break;
894 0 : case FD_GOSSIP_VALUE_CONTACT_INFO: if( FD_UNLIKELY( -1==ser_contact_info( value, out, out_sz ) ) ) return -1; break;
895 :
896 : // UNUSED VALUES, WE DO NOT SERIALIZE THESE
897 : // case FD_GOSSIP_VALUE_LEGACY_CONTACT_INFO: return ser_legacy_contact_info( value, out, out_sz );
898 : // case FD_GOSSIP_VALUE_LOWEST_SLOT: return ser_lowest_slot( value, out, out_sz );
899 : // case FD_GOSSIP_VALUE_LEGACY_SNAPSHOT_HASHES: return ser_legacy_snapshot_hashes( value, out, out_sz );
900 : // case FD_GOSSIP_VALUE_ACCOUNT_HASHES: return ser_account_hashes( value, out, out_sz );
901 : // case FD_GOSSIP_VALUE_EPOCH_SLOTS: return ser_epoch_slots( value, out, out_sz );
902 : // case FD_GOSSIP_VALUE_LEGACY_VERSION: return ser_legacy_version( value, out, out_sz );
903 : // case FD_GOSSIP_VALUE_VERSION: return ser_version( value, out, out_sz );
904 : // case FD_GOSSIP_VALUE_RESTART_LAST_VOTED_FORK_SLOTS: return ser_restart_last_voted_fork_slots( value, out, out_sz );
905 : // case FD_GOSSIP_VALUE_RESTART_HEAVIEST_FORK: return ser_restart_heaviest_fork( value, out, out_sz );
906 0 : default: FD_LOG_CRIT(( "impossible" ));
907 0 : }
908 :
909 0 : return (long)(original_size-_out_sz);
910 0 : }
911 :
912 : long
913 : fd_gossip_pull_request_init( uchar * payload,
914 : ulong payload_sz,
915 : ulong num_keys,
916 : ulong num_bits,
917 : ulong mask,
918 : uint mask_bits,
919 : uchar const * contact_info_crds,
920 : ulong contact_info_crds_sz,
921 : uchar ** out_bloom_keys,
922 : uchar ** out_bloom_bits,
923 0 : uchar ** out_bits_set ) {
924 0 : uchar ** out = &payload;
925 0 : ulong original_size = payload_sz;
926 0 : ulong * out_sz = &payload_sz;
927 :
928 0 : WRITE_U32( FD_GOSSIP_MESSAGE_PULL_REQUEST, out, out_sz );
929 0 : WRITE_U64( num_keys, out, out_sz );
930 0 : *out_bloom_keys = payload+(payload_sz-*out_sz);
931 0 : WRITE_SKIP_BYTES( num_keys*8UL, out, out_sz );
932 :
933 0 : if( FD_LIKELY( !!num_bits ) ) {
934 : /* Bloom bits is a bitvec<u64>, so we need to be careful about converting bloom bits count to vector lengths */
935 0 : ulong bloom_vec_len = (num_bits+63UL)/64UL;
936 0 : WRITE_U8( 1, out, out_sz ); /* has_bits */
937 0 : WRITE_U64( bloom_vec_len, out, out_sz );
938 0 : *out_bloom_bits = payload+(payload_sz-*out_sz);
939 0 : WRITE_SKIP_BYTES( bloom_vec_len*8UL, out, out_sz );
940 0 : } else {
941 0 : WRITE_U8( 0, out, out_sz ); /* has_bits */
942 0 : *out_bloom_bits = NULL;
943 0 : }
944 0 : WRITE_U64( num_bits, out, out_sz );
945 0 : *out_bits_set = payload+(payload_sz-*out_sz);
946 0 : WRITE_SKIP_BYTES( 8UL, out, out_sz );
947 0 : WRITE_U64( mask, out, out_sz );
948 0 : WRITE_U32( mask_bits, out, out_sz );
949 0 : WRITE_BYTES( contact_info_crds, contact_info_crds_sz, out, out_sz );
950 :
951 0 : return (long)(original_size-*out_sz);
952 0 : }
|