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 1173 : #define CHECK( cond ) do { \
28 1155 : if( FD_UNLIKELY( !(cond) ) ) return 0; \
29 1155 : } while( 0 )
30 :
31 102 : #define READ_BYTES( dst, n, payload, payload_sz ) do { \
32 102 : CHECK( (n)<=(*(payload_sz)) ); \
33 102 : fd_memcpy( (dst), *(payload), (n) ); \
34 102 : *(payload) += (n); \
35 102 : *(payload_sz) -= (n); \
36 102 : } while( 0 )
37 :
38 18 : #define SKIP_BYTES( n, payload, payload_sz ) do { \
39 18 : CHECK( (n)<=(*(payload_sz)) ); \
40 18 : *(payload) += (n); \
41 18 : *(payload_sz) -= (n); \
42 18 : } while( 0 )
43 :
44 36 : #define READ_OPTION( dst, payload, payload_sz ) do { \
45 36 : READ_U8( dst, payload, payload_sz ); \
46 36 : CHECK( (dst)==0 || (dst)==1 ); \
47 36 : } while( 0 )
48 :
49 90 : #define READ_ENUM( dst, n, payload, payload_sz ) do { \
50 90 : CHECK( 4UL<=(*(payload_sz)) ); \
51 90 : (dst) = FD_LOAD( uint, *(payload) ); \
52 90 : CHECK( (dst)<n ); \
53 90 : *(payload) += 4UL; \
54 90 : *(payload_sz) -= 4UL; \
55 90 : } while( 0 )
56 :
57 54 : #define READ_U8( dst, payload, payload_sz ) do { \
58 54 : CHECK( 1UL<=(*(payload_sz)) ); \
59 54 : (dst) = FD_LOAD( uchar, *(payload) ); \
60 54 : *(payload) += 1UL; \
61 54 : *(payload_sz) -= 1UL; \
62 54 : } while( 0 )
63 :
64 18 : #define READ_U16( dst, payload, payload_sz ) do { \
65 18 : CHECK( 2UL<=(*(payload_sz)) ); \
66 18 : (dst) = FD_LOAD( ushort, *(payload) ); \
67 18 : *(payload) += 2UL; \
68 18 : *(payload_sz) -= 2UL; \
69 18 : } while( 0 )
70 :
71 54 : #define READ_U32( dst, payload, payload_sz ) do { \
72 54 : CHECK( 4UL<=(*(payload_sz)) ); \
73 54 : (dst) = FD_LOAD( uint, *(payload) ); \
74 54 : *(payload) += 4UL; \
75 54 : *(payload_sz) -= 4UL; \
76 54 : } while( 0 )
77 :
78 219 : #define READ_U64( dst, payload, payload_sz ) do { \
79 219 : CHECK( 8UL<=(*(payload_sz)) ); \
80 219 : (dst) = FD_LOAD( ulong, *(payload) ); \
81 219 : *(payload) += 8UL; \
82 219 : *(payload_sz) -= 8UL; \
83 219 : } while( 0 )
84 :
85 126 : #define READ_U16_VARINT( dst, payload, payload_sz ) do { \
86 126 : ulong _sz = fd_cu16_dec_sz( *(payload), *(payload_sz) ); \
87 126 : CHECK( _sz ); \
88 126 : (dst) = fd_cu16_dec_fixed( *(payload), _sz ); \
89 126 : *(payload) += _sz; \
90 126 : *(payload_sz) -= _sz; \
91 126 : } while( 0 )
92 :
93 18 : #define READ_U64_VARINT( dst, payload, payload_sz ) do { \
94 18 : ulong _val = 0UL; \
95 18 : uint _shift = 0U; \
96 18 : for(;;) { \
97 18 : CHECK( 1UL<=(*(payload_sz)) ); \
98 18 : uchar _byte = FD_LOAD( uchar, *(payload) ); \
99 18 : *(payload) += 1UL; \
100 18 : *(payload_sz) -= 1UL; \
101 18 : _val |= (ulong)(_byte & 0x7F) << _shift; \
102 18 : if( FD_LIKELY( !(_byte & 0x80) ) ) { \
103 18 : CHECK( (_val>>_shift)==(ulong)_byte ); /* last byte not truncated */ \
104 18 : CHECK( _byte || !_shift ); /* no trailing zero bytes */ \
105 18 : (dst) = _val; \
106 18 : break; \
107 18 : } \
108 18 : _shift += 7U; \
109 0 : CHECK( _shift<64U ); \
110 0 : } \
111 18 : } while( 0 )
112 :
113 15 : #define READ_WALLCLOCK( dst, payload, payload_sz ) do { \
114 15 : ulong wallclock_millis; \
115 15 : READ_U64( wallclock_millis, payload, payload_sz ); \
116 15 : CHECK( wallclock_millis<WALLCLOCK_MAX_MILLIS ); \
117 15 : (dst) = wallclock_millis; \
118 15 : } while( 0 )
119 :
120 : static int
121 : deser_vote_instruction( uchar const * data,
122 0 : ulong data_len ) {
123 0 : fd_vote_instruction_t vote_instruction[1];
124 0 : CHECK( fd_vote_instruction_deserialize( vote_instruction, data, data_len ) );
125 0 : CHECK(
126 0 : vote_instruction->discriminant==fd_vote_instruction_enum_vote ||
127 0 : vote_instruction->discriminant==fd_vote_instruction_enum_vote_switch ||
128 0 : vote_instruction->discriminant==fd_vote_instruction_enum_update_vote_state ||
129 0 : vote_instruction->discriminant==fd_vote_instruction_enum_update_vote_state_switch ||
130 0 : vote_instruction->discriminant==fd_vote_instruction_enum_compact_update_vote_state ||
131 0 : vote_instruction->discriminant==fd_vote_instruction_enum_compact_update_vote_state_switch ||
132 0 : vote_instruction->discriminant==fd_vote_instruction_enum_tower_sync ||
133 0 : vote_instruction->discriminant==fd_vote_instruction_enum_tower_sync_switch );
134 : // Oddly, trailing garbage is allowed here at the end of the instruction
135 0 : return 1;
136 0 : }
137 :
138 : static int
139 : deser_vote_txn( fd_gossip_vote_t * vote,
140 : uchar const ** payload,
141 0 : ulong * payload_sz ) {
142 0 : uchar const * payload_start = *payload;
143 :
144 0 : ushort signatures_len;
145 0 : READ_U16_VARINT( signatures_len, payload, payload_sz );
146 0 : SKIP_BYTES( signatures_len*64UL, payload, payload_sz );
147 0 : uchar num_required_signatures, num_readonly_signed_accounts, num_readonly_unsigned_accounts;
148 0 : READ_U8( num_required_signatures, payload, payload_sz );
149 0 : READ_U8( num_readonly_signed_accounts, payload, payload_sz );
150 0 : READ_U8( num_readonly_unsigned_accounts, payload, payload_sz );
151 0 : ushort account_keys_len;
152 0 : READ_U16_VARINT( account_keys_len, payload, payload_sz );
153 0 : uchar const * account_keys = *payload;
154 0 : SKIP_BYTES( account_keys_len*32UL, payload, payload_sz );
155 0 : SKIP_BYTES( 32UL, payload, payload_sz ); /* recent blockhash */
156 0 : ushort instructions_len;
157 0 : READ_U16_VARINT( instructions_len, payload, payload_sz );
158 0 : for( ulong i=0UL; i<instructions_len; i++ ) {
159 0 : uchar program_id_index;
160 0 : READ_U8( program_id_index, payload, payload_sz );
161 0 : CHECK( program_id_index<account_keys_len );
162 0 : CHECK( program_id_index );
163 0 : ushort accounts_len;
164 0 : READ_U16_VARINT( accounts_len, payload, payload_sz );
165 0 : for( ulong j=0UL; j<accounts_len; j++ ) {
166 0 : uchar account_index;
167 0 : READ_U8( account_index, payload, payload_sz );
168 0 : CHECK( account_index<account_keys_len );
169 0 : }
170 0 : ushort data_len;
171 0 : READ_U16_VARINT( data_len, payload, payload_sz );
172 0 : uchar data[ 1232UL ];
173 0 : READ_BYTES( data, data_len, payload, payload_sz );
174 0 : if( FD_LIKELY( i==0UL ) ) {
175 0 : CHECK( accounts_len );
176 0 : uchar const * account_key = account_keys+32UL*program_id_index;
177 0 : CHECK( !memcmp( account_key, fd_solana_vote_program_id.uc, 32UL ) );
178 0 : CHECK( deser_vote_instruction( data, data_len ) );
179 0 : }
180 0 : }
181 :
182 0 : CHECK( num_required_signatures<=signatures_len );
183 0 : CHECK( signatures_len<=account_keys_len );
184 0 : CHECK( num_required_signatures+num_readonly_unsigned_accounts<=account_keys_len );
185 0 : CHECK( num_readonly_signed_accounts<num_required_signatures );
186 0 : CHECK( instructions_len );
187 :
188 0 : vote->transaction_len = (ulong)(*payload-payload_start);
189 0 : fd_memcpy( vote->transaction, payload_start, vote->transaction_len );
190 0 : return 1;
191 0 : }
192 :
193 : static int
194 : deser_vote( fd_gossip_value_t * value,
195 : uchar const ** payload,
196 0 : ulong * payload_sz ) {
197 0 : READ_U8( value->vote->index, payload, payload_sz );
198 0 : CHECK( value->vote->index<FD_GOSSIP_VOTE_IDX_MAX );
199 0 : READ_BYTES( value->origin, 32UL, payload, payload_sz );
200 :
201 0 : CHECK( deser_vote_txn( value->vote, payload, payload_sz ) );
202 0 : READ_WALLCLOCK( value->wallclock, payload, payload_sz );
203 0 : return 1;
204 0 : }
205 :
206 : static int
207 : deser_lowest_slot( fd_gossip_value_t * value,
208 : uchar const ** payload,
209 0 : ulong * payload_sz ) {
210 0 : uchar ix;
211 0 : READ_U8( ix, payload, payload_sz );
212 0 : CHECK( !ix );
213 0 : READ_BYTES( value->origin, 32UL, payload, payload_sz );
214 0 : ulong root;
215 0 : READ_U64( root, payload, payload_sz );
216 0 : CHECK( !root );
217 0 : READ_U64( value->lowest_slot->lowest, payload, payload_sz );
218 0 : CHECK( value->lowest_slot->lowest<MAX_SLOT );
219 0 : ulong slots_len;
220 0 : READ_U64( slots_len, payload, payload_sz );
221 0 : CHECK( !slots_len );
222 0 : ulong stash_len;
223 0 : READ_U64( stash_len, payload, payload_sz );
224 0 : CHECK( !stash_len );
225 0 : READ_WALLCLOCK( value->wallclock, payload, payload_sz );
226 0 : return 1;
227 0 : }
228 :
229 : static int
230 : deser_bitvec_u8_epoch_slots( uchar const ** payload,
231 18 : ulong * payload_sz ) {
232 18 : uchar has_bits;
233 18 : READ_OPTION( has_bits, payload, payload_sz );
234 18 : if( FD_UNLIKELY( !has_bits ) ) {
235 3 : SKIP_BYTES( 8UL, payload, payload_sz );
236 3 : return 1;
237 3 : }
238 :
239 15 : ulong bits_cap;
240 15 : READ_U64( bits_cap, payload, payload_sz );
241 15 : SKIP_BYTES( bits_cap, payload, payload_sz );
242 15 : ulong bits_cnt;
243 15 : READ_U64( bits_cnt, payload, payload_sz );
244 15 : bits_cnt = fd_ulong_min( bits_cnt, bits_cap*8UL );
245 15 : CHECK( bits_cnt==bits_cap*8UL );
246 12 : return 1;
247 15 : }
248 :
249 : static int
250 : deser_epoch_slots( fd_gossip_value_t * value,
251 : uchar const ** payload,
252 18 : ulong * payload_sz ) {
253 18 : READ_U8( value->epoch_slots->index, payload, payload_sz );
254 18 : CHECK( value->epoch_slots->index<FD_GOSSIP_EPOCH_SLOTS_IDX_MAX );
255 18 : READ_BYTES( value->origin, 32UL, payload, payload_sz );
256 18 : ulong slots_len;
257 18 : READ_U64( slots_len, payload, payload_sz );
258 33 : for( ulong i=0UL; i<slots_len; i++ ) {
259 18 : uint is_uncompressed;
260 18 : READ_ENUM( is_uncompressed, 2UL, payload, payload_sz );
261 18 : ulong first_slot;
262 18 : READ_U64( first_slot, payload, payload_sz );
263 18 : CHECK( first_slot<MAX_SLOT );
264 18 : ulong num;
265 18 : READ_U64( num, payload, payload_sz );
266 18 : CHECK( num<MAX_SLOTS_PER_EPOCH_SLOT );
267 18 : if( FD_UNLIKELY( is_uncompressed ) ) {
268 18 : CHECK( deser_bitvec_u8_epoch_slots( payload, payload_sz ) );
269 18 : } else {
270 0 : ulong compressed_len;
271 0 : READ_U64( compressed_len, payload, payload_sz );
272 0 : SKIP_BYTES( compressed_len, payload, payload_sz );
273 0 : }
274 18 : }
275 15 : READ_WALLCLOCK( value->wallclock, payload, payload_sz );
276 15 : return 1;
277 15 : }
278 :
279 : static int
280 : deser_duplicate_shred( fd_gossip_value_t * value,
281 : uchar const ** payload,
282 0 : ulong * payload_sz ) {
283 0 : READ_U16( value->duplicate_shred->index, payload, payload_sz );
284 0 : CHECK( value->duplicate_shred->index<FD_GOSSIP_DUPLICATE_SHRED_IDX_MAX );
285 0 : READ_BYTES( value->origin, 32UL, payload, payload_sz );
286 0 : READ_WALLCLOCK( value->wallclock, payload, payload_sz );
287 0 : READ_U64( value->duplicate_shred->slot, payload, payload_sz );
288 0 : SKIP_BYTES( 5UL, payload, payload_sz ); /* (unused) + shred type (unused) */
289 0 : READ_U8( value->duplicate_shred->num_chunks, payload, payload_sz );
290 0 : READ_U8( value->duplicate_shred->chunk_index, payload, payload_sz );
291 0 : CHECK( value->duplicate_shred->chunk_index<value->duplicate_shred->num_chunks );
292 0 : READ_U64( value->duplicate_shred->chunk_len, payload, payload_sz );
293 0 : READ_BYTES( value->duplicate_shred->chunk, value->duplicate_shred->chunk_len, payload, payload_sz );
294 0 : return 1;
295 0 : }
296 :
297 : static int
298 : deser_snapshot_hashes( fd_gossip_value_t * value,
299 : uchar const ** payload,
300 0 : ulong * payload_sz ) {
301 0 : READ_BYTES( value->origin, 32UL, payload, payload_sz );
302 0 : READ_U64( value->snapshot_hashes->full_slot, payload, payload_sz );
303 0 : CHECK( value->snapshot_hashes->full_slot<MAX_SLOT );
304 0 : READ_BYTES( value->snapshot_hashes->full_hash, 32UL, payload, payload_sz );
305 0 : READ_U64( value->snapshot_hashes->incremental_len, payload, payload_sz );
306 0 : for( ulong i=0UL; i<value->snapshot_hashes->incremental_len; i++ ) {
307 0 : READ_U64( value->snapshot_hashes->incremental[ i ].slot, payload, payload_sz );
308 0 : CHECK( value->snapshot_hashes->incremental[ i ].slot<MAX_SLOT );
309 0 : CHECK( value->snapshot_hashes->incremental[ i ].slot>value->snapshot_hashes->full_slot );
310 0 : READ_BYTES( value->snapshot_hashes->incremental[ i ].hash, 32UL, payload, payload_sz );
311 0 : }
312 0 : READ_WALLCLOCK( value->wallclock, payload, payload_sz );
313 0 : return 1;
314 0 : }
315 :
316 : static int
317 : deser_contact_info( fd_gossip_value_t * value,
318 : uchar const ** payload,
319 18 : ulong * payload_sz ) {
320 18 : READ_BYTES( value->origin, 32UL, payload, payload_sz );
321 18 : READ_U64_VARINT( value->wallclock, payload, payload_sz );
322 18 : CHECK( value->wallclock<WALLCLOCK_MAX_MILLIS );
323 18 : READ_U64( value->contact_info->outset, payload, payload_sz );
324 18 : READ_U16( value->contact_info->shred_version, payload, payload_sz );
325 18 : READ_U16_VARINT( value->contact_info->version.major, payload, payload_sz );
326 18 : READ_U16_VARINT( value->contact_info->version.minor, payload, payload_sz );
327 18 : READ_U16_VARINT( value->contact_info->version.patch, payload, payload_sz );
328 18 : READ_U32( value->contact_info->version.commit, payload, payload_sz );
329 18 : READ_U32( value->contact_info->version.feature_set, payload, payload_sz );
330 18 : READ_U16_VARINT( value->contact_info->version.client, payload, payload_sz );
331 :
332 : /* Tightest bounds for array sizes given network constraints.
333 :
334 : IPv6 minimum MTU = 1280
335 : IPv6 header = 40
336 : UDP header = 8
337 : PACKET_DATA_SIZE = 1232 (= 1280 - 40 - 8)
338 :
339 : Bytes consumed for preamble:
340 : Push/Pull Response:
341 : Protocol tag(4) + from(32) + values_len(8) = 44
342 : Pull Request:
343 : Protocol tag(4) + keys_len(8) + bloom_none(9) +
344 : num_bits_set(8) + mask(8) + mask_bits(4) = 41
345 : Minimum consumed for preamble: 41
346 :
347 : Bytes consumed before addrs loop:
348 : Preamble(41) + signature(64) +
349 : CrdsData tag(4) + origin(32) + wallclock_varint(1) + outset(8) +
350 : shred_version(2) + major(1) + minor(1) + patch(1) + commit(4) +
351 : feature_set(4) + client(1) + addrs_len_varint(1) = 165
352 :
353 : Remaining: 1232 - 165 = 1067
354 : Each addr: READ_ENUM(4) + READ_U32(4) = 8 bytes minimum
355 : Max addrs = floor(1067/8) = 133
356 :
357 : Bytes consumed before sockets loop:
358 : (same as above) + sockets_len_varint(1) = 166
359 :
360 : Remaining: 1232 - 166 = 1066
361 : Each socket: READ_U8(1) + READ_U8(1) + READ_U16_VARINT(1) = 3 bytes minimum
362 : Max sockets = floor(1066/3) = 355 */
363 :
364 18 : #define FD_GOSSIP_CONTACT_INFO_MAX_ADDRESSES (133UL)
365 18 : #define FD_GOSSIP_CONTACT_INFO_MAX_SOCKETS (355UL)
366 :
367 18 : uint is_ip6[ FD_GOSSIP_CONTACT_INFO_MAX_ADDRESSES ];
368 18 : union {
369 18 : uint ip4;
370 18 : uchar ip6[ 16UL ];
371 18 : } ips[ FD_GOSSIP_CONTACT_INFO_MAX_ADDRESSES ];
372 :
373 18 : ulong addrs_len;
374 18 : READ_U16_VARINT( addrs_len, payload, payload_sz );
375 18 : for( ulong i=0UL; i<addrs_len; i++ ) {
376 0 : READ_ENUM( is_ip6[ i ], 2UL, payload, payload_sz );
377 0 : if( !is_ip6[ i ] ) READ_U32( ips[ i ].ip4, payload, payload_sz );
378 0 : else READ_BYTES( ips[ i ].ip6, 16UL, payload, payload_sz );
379 0 : }
380 :
381 18 : struct {
382 18 : uchar key;
383 18 : uchar index;
384 18 : ushort offset;
385 18 : } sockets[ FD_GOSSIP_CONTACT_INFO_MAX_SOCKETS ];
386 :
387 18 : ulong sockets_len;
388 18 : READ_U16_VARINT( sockets_len, payload, payload_sz );
389 18 : for( ulong i=0UL; i<sockets_len; i++ ) {
390 0 : READ_U8( sockets[ i ].key, payload, payload_sz );
391 0 : READ_U8( sockets[ i ].index, payload, payload_sz );
392 0 : READ_U16_VARINT( sockets[ i ].offset, payload, payload_sz );
393 0 : }
394 :
395 18 : ulong extensions_len;
396 18 : READ_U16_VARINT( extensions_len, payload, payload_sz );
397 18 : for( ulong i=0UL; i<extensions_len; i++ ) {
398 0 : SKIP_BYTES( 1UL, payload, payload_sz ); /* type */
399 0 : ushort bytes_len;
400 0 : READ_U16_VARINT( bytes_len, payload, payload_sz );
401 0 : SKIP_BYTES( bytes_len, payload, payload_sz );
402 0 : }
403 :
404 : /* Ipv6 and duplicate IPs are not allowed
405 : https://github.com/anza-xyz/agave/blob/v4.2.0-beta.0/gossip/src/contact_info.rs#L667-L676 */
406 18 : for( ulong i=0UL; i<addrs_len; i++ ) {
407 0 : CHECK( !is_ip6[ i ] );
408 0 : for( ulong j=0UL; j<i; j++ ) {
409 0 : CHECK( ips[ i ].ip4!=ips[ j ].ip4 );
410 0 : }
411 0 : }
412 :
413 : /* Each socket must reference unique key */
414 18 : int seen_socket_key[ 256UL ] = {0};
415 18 : for( ulong i=0UL; i<sockets_len; i++ ) {
416 0 : CHECK( !seen_socket_key[ sockets[ i ].key ] );
417 0 : seen_socket_key[ sockets[ i ].key ] = 1;
418 0 : }
419 :
420 : /* Each IP address must be referenced by at least one socket */
421 18 : int seen_ip_addr[ FD_GOSSIP_CONTACT_INFO_MAX_ADDRESSES ] = {0};
422 18 : for( ulong i=0UL; i<sockets_len; i++ ) {
423 0 : CHECK( sockets[ i ].index<addrs_len );
424 0 : seen_ip_addr[ sockets[ i ].index ] = 1;
425 0 : }
426 18 : for( ulong i=0UL; i<addrs_len; i++ ) CHECK( seen_ip_addr[ i ] );
427 :
428 : /* Port offsets don't overflow */
429 18 : ushort cur_port = 0U;
430 18 : for( ulong i=0UL; i<sockets_len; i++ ) {
431 0 : ushort result;
432 0 : CHECK( !__builtin_add_overflow( cur_port, sockets[ i ].offset, &result ) );
433 0 : cur_port = result;
434 0 : }
435 :
436 18 : memset( value->contact_info->sockets, 0, sizeof( value->contact_info->sockets ) );
437 :
438 18 : cur_port = 0U;
439 18 : for( ulong i=0UL; i<sockets_len; i++ ) {
440 0 : cur_port = (ushort)(cur_port + sockets[ i ].offset);
441 0 : if( FD_LIKELY( sockets[ i ].key<FD_GOSSIP_CONTACT_INFO_SOCKET_CNT ) ) {
442 0 : value->contact_info->sockets[ sockets[ i ].key ].is_ipv6 = is_ip6[ sockets[ i ].index ];
443 0 : if( FD_LIKELY( !is_ip6[ sockets[ i ].index ] ) ) value->contact_info->sockets[ sockets[ i ].key ].ip4 = ips[ sockets[ i ].index ].ip4;
444 0 : else fd_memcpy( value->contact_info->sockets[ sockets[ i ].key ].ip6, ips[ sockets[ i ].index ].ip6, 16UL );
445 :
446 0 : value->contact_info->sockets[ sockets[ i ].key ].port = fd_ushort_bswap( cur_port );
447 0 : }
448 0 : }
449 18 : return 1;
450 18 : }
451 :
452 : /* wincode returns a default (empty) bitvec for None and also relaxes
453 : length checks.
454 : https://github.com/anza-xyz/wincode/blob/wincode%40v0.5.5/wincode/src/schema/external/bv.rs#L68-L81 */
455 : static int
456 : deser_bitvec_u8_restart_last_voted_fork_slots( uchar const ** payload,
457 0 : ulong * payload_sz ) {
458 0 : uchar has_bits;
459 0 : READ_OPTION( has_bits, payload, payload_sz );
460 0 : if( FD_UNLIKELY( !has_bits ) ) {
461 0 : SKIP_BYTES( 8UL, payload, payload_sz );
462 0 : return 1;
463 0 : }
464 :
465 0 : ulong bits_cap;
466 0 : READ_U64( bits_cap, payload, payload_sz );
467 0 : SKIP_BYTES( bits_cap, payload, payload_sz );
468 0 : SKIP_BYTES( 8UL, payload, payload_sz );
469 0 : return 1;
470 0 : }
471 :
472 : static int
473 : deser_restart_last_voted_fork_slots( fd_gossip_value_t * value,
474 : uchar const ** payload,
475 0 : ulong * payload_sz ) {
476 0 : READ_BYTES( value->origin, 32UL, payload, payload_sz );
477 0 : READ_WALLCLOCK( value->wallclock, payload, payload_sz );
478 0 : uint is_raw_offsets;
479 0 : READ_ENUM( is_raw_offsets, 2UL, payload, payload_sz );
480 0 : if( FD_LIKELY( is_raw_offsets ) ) {
481 0 : CHECK( deser_bitvec_u8_restart_last_voted_fork_slots( payload, payload_sz ) );
482 0 : } else {
483 0 : ulong slots_len;
484 0 : READ_U64( slots_len, payload, payload_sz );
485 0 : for( ulong i=0UL; i<slots_len; i++ ) {
486 0 : ushort _slot;
487 0 : READ_U16_VARINT( _slot, payload, payload_sz );
488 0 : (void)_slot;
489 0 : }
490 0 : }
491 0 : SKIP_BYTES( 8UL+32UL+2UL, payload, payload_sz ); /* last voted slot + last voted hash + shred version */
492 0 : return 1;
493 0 : }
494 :
495 : static int
496 : deser_restart_heaviest_fork( fd_gossip_value_t * value,
497 : uchar const ** payload,
498 0 : ulong * payload_sz ) {
499 0 : READ_BYTES( value->origin, 32UL, payload, payload_sz );
500 0 : READ_WALLCLOCK( value->wallclock, payload, payload_sz );
501 0 : SKIP_BYTES( 8UL+32UL+8UL+2UL, payload, payload_sz ); /* last slot + last slot hash + observed stake + shred version */
502 0 : return 1;
503 0 : }
504 :
505 : static int
506 : deser_value( fd_gossip_value_t * value,
507 : uchar const ** payload,
508 36 : ulong * payload_sz ) {
509 36 : READ_BYTES( value->signature, 64UL, payload, payload_sz );
510 36 : READ_ENUM( value->tag, FD_GOSSIP_VALUE_CNT, payload, payload_sz );
511 :
512 36 : switch( value->tag ) {
513 0 : 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 */
514 0 : case FD_GOSSIP_VALUE_VOTE: return deser_vote( value, payload, payload_sz );
515 0 : case FD_GOSSIP_VALUE_LOWEST_SLOT: return deser_lowest_slot( value, payload, payload_sz );
516 0 : 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 */
517 0 : 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 */
518 18 : case FD_GOSSIP_VALUE_EPOCH_SLOTS: return deser_epoch_slots( value, payload, payload_sz );
519 0 : 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 */
520 0 : 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 */
521 0 : 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 */
522 0 : case FD_GOSSIP_VALUE_DUPLICATE_SHRED: return deser_duplicate_shred( value, payload, payload_sz );
523 0 : case FD_GOSSIP_VALUE_SNAPSHOT_HASHES: return deser_snapshot_hashes( value, payload, payload_sz );
524 18 : case FD_GOSSIP_VALUE_CONTACT_INFO: return deser_contact_info( value, payload, payload_sz );
525 0 : case FD_GOSSIP_VALUE_RESTART_LAST_VOTED_FORK_SLOTS: return deser_restart_last_voted_fork_slots( value, payload, payload_sz );
526 0 : case FD_GOSSIP_VALUE_RESTART_HEAVIEST_FORK: return deser_restart_heaviest_fork( value, payload, payload_sz );
527 0 : default: FD_LOG_CRIT(( "impossible" ));
528 36 : }
529 36 : }
530 :
531 : /* wincode returns a default bitvec for None and also relaxes length
532 : checks. For Some, it additionally truncates the bitvec bits_len to
533 : the bits_cap*64 if bits_len is larger.
534 : https://github.com/anza-xyz/wincode/blob/wincode%40v0.5.5/wincode/src/schema/external/bv.rs#L68-L81 */
535 : static int
536 : deser_bitvec_u64( fd_gossip_bloom_t * bloom,
537 : uchar const ** payload,
538 18 : ulong * payload_sz ) {
539 18 : uchar has_bits;
540 18 : READ_OPTION( has_bits, payload, payload_sz );
541 18 : if( FD_UNLIKELY( !has_bits ) ) {
542 6 : bloom->bits_cap = 0UL;
543 6 : READ_U64( bloom->bits_len, payload, payload_sz );
544 6 : bloom->bits_len = 0UL;
545 6 : return 1;
546 6 : }
547 :
548 12 : READ_U64( bloom->bits_cap, payload, payload_sz );
549 12 : ulong dummy;
550 12 : CHECK( !__builtin_mul_overflow( bloom->bits_cap, 8UL, &dummy ) );
551 12 : READ_BYTES( bloom->bits, bloom->bits_cap*8UL, payload, payload_sz );
552 12 : READ_U64( bloom->bits_len, payload, payload_sz );
553 12 : bloom->bits_len = fd_ulong_min( bloom->bits_len, bloom->bits_cap*64UL );
554 12 : return 1;
555 12 : }
556 :
557 : static int
558 : deser_pull_request( fd_gossip_message_t * message,
559 : uchar const ** payload,
560 : ulong * payload_sz,
561 18 : ulong original_sz ) {
562 18 : READ_U64( message->pull_request->crds_filter->filter->keys_len, payload, payload_sz );
563 18 : for( ulong i=0UL; i<message->pull_request->crds_filter->filter->keys_len; i++ ) {
564 0 : READ_U64( message->pull_request->crds_filter->filter->keys[ i ], payload, payload_sz );
565 0 : }
566 :
567 18 : CHECK( deser_bitvec_u64( message->pull_request->crds_filter->filter, payload, payload_sz ) );
568 :
569 18 : READ_U64( message->pull_request->crds_filter->filter->num_bits_set, payload, payload_sz );
570 18 : READ_U64( message->pull_request->crds_filter->mask, payload, payload_sz );
571 18 : 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 18 : CHECK( message->pull_request->crds_filter->mask_bits>=FD_GOSSIP_MIN_PULL_REQUEST_MASK_BITS );
575 :
576 18 : message->pull_request->contact_info->offset = original_sz-*payload_sz;
577 18 : CHECK( deser_value( message->pull_request->contact_info, payload, payload_sz ) );
578 18 : 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 18 : CHECK( message->pull_request->contact_info->tag==FD_GOSSIP_VALUE_CONTACT_INFO );
581 18 : return 1;
582 18 : }
583 :
584 : static int
585 : deser_pull_response( fd_gossip_message_t * message,
586 : uchar const ** payload,
587 : ulong * payload_sz,
588 0 : ulong original_sz ) {
589 0 : READ_BYTES( message->pull_response->from, 32UL, payload, payload_sz );
590 0 : READ_U64( message->pull_response->values_len, payload, payload_sz );
591 0 : for( ulong i=0UL; i<message->pull_response->values_len; i++ ) {
592 0 : message->pull_response->values[ i ].offset = original_sz-*payload_sz;
593 0 : CHECK( deser_value( &message->pull_response->values[ i ], payload, payload_sz ) );
594 0 : message->pull_response->values[ i ].length = original_sz-*payload_sz-message->pull_response->values[ i ].offset;
595 0 : }
596 0 : return 1;
597 0 : }
598 :
599 : static int
600 : deser_push( fd_gossip_message_t * message,
601 : uchar const ** payload,
602 : ulong * payload_sz,
603 18 : ulong original_sz ) {
604 18 : READ_BYTES( message->push->from, 32UL, payload, payload_sz );
605 18 : READ_U64( message->push->values_len, payload, payload_sz );
606 33 : for( ulong i=0UL; i<message->push->values_len; i++ ) {
607 18 : message->push->values[ i ].offset = original_sz-*payload_sz;
608 18 : CHECK( deser_value( &message->push->values[ i ], payload, payload_sz ) );
609 15 : message->push->values[ i ].length = original_sz-*payload_sz-message->push->values[ i ].offset;
610 15 : }
611 15 : return 1;
612 18 : }
613 :
614 : static int
615 : deser_prune( fd_gossip_message_t * message,
616 : uchar const ** payload,
617 0 : ulong * payload_sz ) {
618 0 : READ_BYTES( message->prune->sender, 32UL, payload, payload_sz );
619 0 : READ_BYTES( message->prune->pubkey, 32UL, payload, payload_sz );
620 0 : CHECK( !memcmp( message->prune->sender, message->prune->pubkey, 32UL ) );
621 0 : READ_U64( message->prune->prunes_len, payload, payload_sz );
622 0 : for( ulong i=0UL; i<message->prune->prunes_len; i++ ) {
623 0 : READ_BYTES( message->prune->prunes[ i ], 32UL, payload, payload_sz );
624 0 : }
625 0 : READ_BYTES( message->prune->signature, 64UL, payload, payload_sz );
626 0 : READ_BYTES( message->prune->destination, 32UL, payload, payload_sz );
627 0 : READ_WALLCLOCK( message->prune->wallclock, payload, payload_sz );
628 0 : return 1;
629 0 : }
630 :
631 : static int
632 : deser_ping( fd_gossip_message_t * message,
633 : uchar const ** payload,
634 0 : ulong * payload_sz ) {
635 0 : READ_BYTES( message->ping->from, 32UL, payload, payload_sz );
636 0 : READ_BYTES( message->ping->token, 32UL, payload, payload_sz );
637 0 : READ_BYTES( message->ping->signature, 64UL, payload, payload_sz );
638 0 : return 1;
639 0 : }
640 :
641 : static int
642 : deser_pong( fd_gossip_message_t * message,
643 : uchar const ** payload,
644 0 : ulong * payload_sz ) {
645 0 : READ_BYTES( message->pong->from, 32UL, payload, payload_sz );
646 0 : READ_BYTES( message->pong->hash, 32UL, payload, payload_sz );
647 0 : READ_BYTES( message->pong->signature, 64UL, payload, payload_sz );
648 0 : return 1;
649 0 : }
650 :
651 : int
652 : fd_gossip_message_deserialize( fd_gossip_message_t * message,
653 : uchar const * _payload,
654 36 : ulong _payload_sz ) {
655 36 : uchar const ** payload = &_payload;
656 36 : ulong * payload_sz = &_payload_sz;
657 36 : ulong original_sz = _payload_sz;
658 :
659 36 : CHECK( _payload_sz<=1232UL );
660 36 : READ_ENUM( message->tag, FD_GOSSIP_MESSAGE_CNT, payload, payload_sz );
661 :
662 36 : switch( message->tag ){
663 18 : case FD_GOSSIP_MESSAGE_PULL_REQUEST: CHECK( deser_pull_request( message, payload, payload_sz, original_sz ) ); break;
664 18 : case FD_GOSSIP_MESSAGE_PULL_RESPONSE: CHECK( deser_pull_response( message, payload, payload_sz, original_sz ) ); break;
665 18 : case FD_GOSSIP_MESSAGE_PUSH: CHECK( deser_push( message, payload, payload_sz, original_sz ) ); break;
666 15 : case FD_GOSSIP_MESSAGE_PRUNE: CHECK( deser_prune( message, payload, payload_sz ) ); break;
667 0 : case FD_GOSSIP_MESSAGE_PING: CHECK( deser_ping( message, payload, payload_sz ) ); break;
668 0 : case FD_GOSSIP_MESSAGE_PONG: CHECK( deser_pong( message, payload, payload_sz ) ); break;
669 0 : default: FD_LOG_CRIT(( "invalid message tag" ));
670 36 : }
671 :
672 33 : return !*payload_sz;
673 36 : }
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 : ulong ** out_bloom_keys,
922 : ulong ** out_bloom_bits,
923 0 : ulong ** 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 = fd_type_pun( 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 = fd_type_pun( 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 = fd_type_pun( 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 : }
|