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 1140 : #define CHECK( cond ) do { \
28 1128 : if( FD_UNLIKELY( !(cond) ) ) return 0; \
29 1128 : } 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 15 : #define SKIP_BYTES( n, payload, payload_sz ) do { \
39 15 : CHECK( (n)<=(*(payload_sz)) ); \
40 15 : *(payload) += (n); \
41 15 : *(payload_sz) -= (n); \
42 15 : } while( 0 )
43 :
44 42 : #define READ_OPTION( dst, payload, payload_sz ) do { \
45 42 : READ_U8( dst, payload, payload_sz ); \
46 42 : CHECK( (dst)==0 || (dst)==1 ); \
47 42 : } while( 0 )
48 :
49 96 : #define READ_ENUM( dst, n, payload, payload_sz ) do { \
50 96 : CHECK( 4UL<=(*(payload_sz)) ); \
51 96 : (dst) = FD_LOAD( uint, *(payload) ); \
52 96 : CHECK( (dst)<n ); \
53 96 : *(payload) += 4UL; \
54 96 : *(payload_sz) -= 4UL; \
55 96 : } while( 0 )
56 :
57 63 : #define READ_U8( dst, payload, payload_sz ) do { \
58 63 : CHECK( 1UL<=(*(payload_sz)) ); \
59 63 : (dst) = FD_LOAD( uchar, *(payload) ); \
60 63 : *(payload) += 1UL; \
61 63 : *(payload_sz) -= 1UL; \
62 63 : } while( 0 )
63 :
64 12 : #define READ_U16( dst, payload, payload_sz ) do { \
65 12 : CHECK( 2UL<=(*(payload_sz)) ); \
66 12 : (dst) = FD_LOAD( ushort, *(payload) ); \
67 12 : *(payload) += 2UL; \
68 12 : *(payload_sz) -= 2UL; \
69 12 : } while( 0 )
70 :
71 36 : #define READ_U32( dst, payload, payload_sz ) do { \
72 36 : CHECK( 4UL<=(*(payload_sz)) ); \
73 36 : (dst) = FD_LOAD( uint, *(payload) ); \
74 36 : *(payload) += 4UL; \
75 36 : *(payload_sz) -= 4UL; \
76 36 : } while( 0 )
77 :
78 222 : #define READ_U64( dst, payload, payload_sz ) do { \
79 222 : CHECK( 8UL<=(*(payload_sz)) ); \
80 222 : (dst) = FD_LOAD( ulong, *(payload) ); \
81 222 : *(payload) += 8UL; \
82 222 : *(payload_sz) -= 8UL; \
83 222 : } while( 0 )
84 :
85 84 : #define READ_U16_VARINT( dst, payload, payload_sz ) do { \
86 84 : ulong _sz = fd_cu16_dec_sz( *(payload), *(payload_sz) ); \
87 84 : CHECK( _sz ); \
88 84 : (dst) = fd_cu16_dec_fixed( *(payload), _sz ); \
89 84 : *(payload) += _sz; \
90 84 : *(payload_sz) -= _sz; \
91 84 : } while( 0 )
92 :
93 12 : #define READ_U64_VARINT( dst, payload, payload_sz ) do { \
94 12 : ulong _val = 0UL; \
95 12 : uint _shift = 0U; \
96 12 : for(;;) { \
97 12 : CHECK( 1UL<=(*(payload_sz)) ); \
98 12 : uchar _byte = FD_LOAD( uchar, *(payload) ); \
99 12 : *(payload) += 1UL; \
100 12 : *(payload_sz) -= 1UL; \
101 12 : _val |= (ulong)(_byte & 0x7F) << _shift; \
102 12 : if( FD_LIKELY( !(_byte & 0x80) ) ) { \
103 12 : CHECK( (_val>>_shift)==(ulong)_byte ); /* last byte not truncated */ \
104 12 : CHECK( _byte || !_shift ); /* no trailing zero bytes */ \
105 12 : (dst) = _val; \
106 12 : break; \
107 12 : } \
108 12 : _shift += 7U; \
109 0 : CHECK( _shift<64U ); \
110 0 : } \
111 12 : } while( 0 )
112 :
113 9 : #define READ_WALLCLOCK( dst, payload, payload_sz ) do { \
114 9 : ulong wallclock_millis; \
115 9 : READ_U64( wallclock_millis, payload, payload_sz ); \
116 9 : CHECK( wallclock_millis<WALLCLOCK_MAX_MILLIS ); \
117 9 : (dst) = wallclock_millis; \
118 9 : } 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 21 : ulong * payload_sz ) {
232 21 : uchar has_bits;
233 21 : READ_OPTION( has_bits, payload, payload_sz );
234 21 : if( FD_UNLIKELY( !has_bits ) ) {
235 6 : ulong bits_cnt;
236 6 : READ_U64( bits_cnt, payload, payload_sz );
237 6 : CHECK( !bits_cnt );
238 3 : return 1;
239 6 : }
240 :
241 15 : ulong bits_cap;
242 15 : READ_U64( bits_cap, payload, payload_sz );
243 15 : SKIP_BYTES( bits_cap, payload, payload_sz );
244 15 : ulong bits_cnt;
245 15 : READ_U64( bits_cnt, payload, payload_sz );
246 15 : CHECK( bits_cnt==bits_cap*8UL );
247 6 : return 1;
248 15 : }
249 :
250 : static int
251 : deser_epoch_slots( fd_gossip_value_t * value,
252 : uchar const ** payload,
253 21 : ulong * payload_sz ) {
254 21 : READ_U8( value->epoch_slots->index, payload, payload_sz );
255 21 : CHECK( value->epoch_slots->index<FD_GOSSIP_EPOCH_SLOTS_IDX_MAX );
256 21 : READ_BYTES( value->origin, 32UL, payload, payload_sz );
257 21 : ulong slots_len;
258 21 : READ_U64( slots_len, payload, payload_sz );
259 30 : for( ulong i=0UL; i<slots_len; i++ ) {
260 21 : uint is_uncompressed;
261 21 : READ_ENUM( is_uncompressed, 2UL, payload, payload_sz );
262 21 : ulong first_slot;
263 21 : READ_U64( first_slot, payload, payload_sz );
264 21 : CHECK( first_slot<MAX_SLOT );
265 21 : ulong num;
266 21 : READ_U64( num, payload, payload_sz );
267 21 : CHECK( num<MAX_SLOTS_PER_EPOCH_SLOT );
268 21 : if( FD_UNLIKELY( is_uncompressed ) ) {
269 21 : CHECK( deser_bitvec_u8_epoch_slots( payload, payload_sz ) );
270 21 : } else {
271 0 : ulong compressed_len;
272 0 : READ_U64( compressed_len, payload, payload_sz );
273 0 : SKIP_BYTES( compressed_len, payload, payload_sz );
274 0 : }
275 21 : }
276 9 : READ_WALLCLOCK( value->wallclock, payload, payload_sz );
277 9 : return 1;
278 9 : }
279 :
280 : static int
281 : deser_duplicate_shred( fd_gossip_value_t * value,
282 : uchar const ** payload,
283 0 : ulong * payload_sz ) {
284 0 : READ_U16( value->duplicate_shred->index, payload, payload_sz );
285 0 : CHECK( value->duplicate_shred->index<FD_GOSSIP_DUPLICATE_SHRED_IDX_MAX );
286 0 : READ_BYTES( value->origin, 32UL, payload, payload_sz );
287 0 : READ_WALLCLOCK( value->wallclock, payload, payload_sz );
288 0 : READ_U64( value->duplicate_shred->slot, payload, payload_sz );
289 0 : SKIP_BYTES( 5UL, payload, payload_sz ); /* (unused) + shred type (unused) */
290 0 : READ_U8( value->duplicate_shred->num_chunks, payload, payload_sz );
291 0 : READ_U8( value->duplicate_shred->chunk_index, payload, payload_sz );
292 0 : CHECK( value->duplicate_shred->chunk_index<value->duplicate_shred->num_chunks );
293 0 : READ_U64( value->duplicate_shred->chunk_len, payload, payload_sz );
294 0 : READ_BYTES( value->duplicate_shred->chunk, value->duplicate_shred->chunk_len, payload, payload_sz );
295 0 : return 1;
296 0 : }
297 :
298 : static int
299 : deser_snapshot_hashes( fd_gossip_value_t * value,
300 : uchar const ** payload,
301 0 : ulong * payload_sz ) {
302 0 : READ_BYTES( value->origin, 32UL, payload, payload_sz );
303 0 : READ_U64( value->snapshot_hashes->full_slot, payload, payload_sz );
304 0 : CHECK( value->snapshot_hashes->full_slot<MAX_SLOT );
305 0 : READ_BYTES( value->snapshot_hashes->full_hash, 32UL, payload, payload_sz );
306 0 : READ_U64( value->snapshot_hashes->incremental_len, payload, payload_sz );
307 0 : for( ulong i=0UL; i<value->snapshot_hashes->incremental_len; i++ ) {
308 0 : READ_U64( value->snapshot_hashes->incremental[ i ].slot, payload, payload_sz );
309 0 : CHECK( value->snapshot_hashes->incremental[ i ].slot<MAX_SLOT );
310 0 : CHECK( value->snapshot_hashes->incremental[ i ].slot>value->snapshot_hashes->full_slot );
311 0 : READ_BYTES( value->snapshot_hashes->incremental[ i ].hash, 32UL, payload, payload_sz );
312 0 : }
313 0 : READ_WALLCLOCK( value->wallclock, payload, payload_sz );
314 0 : return 1;
315 0 : }
316 :
317 : static int
318 : deser_contact_info( fd_gossip_value_t * value,
319 : uchar const ** payload,
320 12 : ulong * payload_sz ) {
321 12 : READ_BYTES( value->origin, 32UL, payload, payload_sz );
322 12 : READ_U64_VARINT( value->wallclock, payload, payload_sz );
323 12 : CHECK( value->wallclock<WALLCLOCK_MAX_MILLIS );
324 12 : READ_U64( value->contact_info->outset, payload, payload_sz );
325 12 : READ_U16( value->contact_info->shred_version, payload, payload_sz );
326 12 : READ_U16_VARINT( value->contact_info->version.major, payload, payload_sz );
327 12 : READ_U16_VARINT( value->contact_info->version.minor, payload, payload_sz );
328 12 : READ_U16_VARINT( value->contact_info->version.patch, payload, payload_sz );
329 12 : READ_U32( value->contact_info->version.commit, payload, payload_sz );
330 12 : READ_U32( value->contact_info->version.feature_set, payload, payload_sz );
331 12 : 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 12 : #define FD_GOSSIP_CONTACT_INFO_MAX_ADDRESSES (133UL)
366 12 : #define FD_GOSSIP_CONTACT_INFO_MAX_SOCKETS (355UL)
367 :
368 12 : uint is_ip6[ FD_GOSSIP_CONTACT_INFO_MAX_ADDRESSES ];
369 12 : union {
370 12 : uint ip4;
371 12 : uchar ip6[ 16UL ];
372 12 : } ips[ FD_GOSSIP_CONTACT_INFO_MAX_ADDRESSES ];
373 :
374 12 : ulong addrs_len;
375 12 : READ_U16_VARINT( addrs_len, payload, payload_sz );
376 12 : for( ulong i=0UL; i<addrs_len; i++ ) {
377 0 : READ_ENUM( is_ip6[ i ], 2UL, payload, payload_sz );
378 0 : if( !is_ip6[ i ] ) READ_U32( ips[ i ].ip4, payload, payload_sz );
379 0 : else READ_BYTES( ips[ i ].ip6, 16UL, payload, payload_sz );
380 0 : }
381 :
382 12 : struct {
383 12 : uchar key;
384 12 : uchar index;
385 12 : ushort offset;
386 12 : } sockets[ FD_GOSSIP_CONTACT_INFO_MAX_SOCKETS ];
387 :
388 12 : ulong sockets_len;
389 12 : READ_U16_VARINT( sockets_len, payload, payload_sz );
390 12 : for( ulong i=0UL; i<sockets_len; i++ ) {
391 0 : READ_U8( sockets[ i ].key, payload, payload_sz );
392 0 : READ_U8( sockets[ i ].index, payload, payload_sz );
393 0 : READ_U16_VARINT( sockets[ i ].offset, payload, payload_sz );
394 0 : }
395 :
396 12 : ulong extensions_len;
397 12 : READ_U16_VARINT( extensions_len, payload, payload_sz );
398 12 : 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 12 : for( ulong i=0UL; i<addrs_len; i++ ) {
408 0 : CHECK( !is_ip6[ i ] );
409 0 : for( ulong j=0UL; j<i; j++ ) {
410 0 : CHECK( ips[ i ].ip4!=ips[ j ].ip4 );
411 0 : }
412 0 : }
413 :
414 : /* Each socket must reference unique key */
415 12 : int seen_socket_key[ 256UL ] = {0};
416 12 : for( ulong i=0UL; i<sockets_len; i++ ) {
417 0 : CHECK( !seen_socket_key[ sockets[ i ].key ] );
418 0 : seen_socket_key[ sockets[ i ].key ] = 1;
419 0 : }
420 :
421 : /* Each IP address must be referenced by at least one socket */
422 12 : int seen_ip_addr[ FD_GOSSIP_CONTACT_INFO_MAX_ADDRESSES ] = {0};
423 12 : for( ulong i=0UL; i<sockets_len; i++ ) {
424 0 : CHECK( sockets[ i ].index<addrs_len );
425 0 : seen_ip_addr[ sockets[ i ].index ] = 1;
426 0 : }
427 12 : for( ulong i=0UL; i<addrs_len; i++ ) CHECK( seen_ip_addr[ i ] );
428 :
429 : /* Port offsets don't overflow */
430 12 : ushort cur_port = 0U;
431 12 : for( ulong i=0UL; i<sockets_len; i++ ) {
432 0 : ushort result;
433 0 : CHECK( !__builtin_add_overflow( cur_port, sockets[ i ].offset, &result ) );
434 0 : cur_port = result;
435 0 : }
436 :
437 12 : memset( value->contact_info->sockets, 0, sizeof( value->contact_info->sockets ) );
438 :
439 12 : cur_port = 0U;
440 12 : for( ulong i=0UL; i<sockets_len; i++ ) {
441 0 : cur_port = (ushort)(cur_port + sockets[ i ].offset);
442 0 : if( FD_LIKELY( sockets[ i ].key<FD_GOSSIP_CONTACT_INFO_SOCKET_CNT ) ) {
443 0 : value->contact_info->sockets[ sockets[ i ].key ].is_ipv6 = is_ip6[ sockets[ i ].index ];
444 0 : 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 0 : value->contact_info->sockets[ sockets[ i ].key ].port = fd_ushort_bswap( cur_port );
448 0 : }
449 0 : }
450 12 : return 1;
451 12 : }
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 0 : ulong * payload_sz ) {
457 0 : uchar has_bits;
458 0 : READ_OPTION( has_bits, payload, payload_sz );
459 0 : if( FD_UNLIKELY( !has_bits ) ) {
460 0 : ulong bits_len;
461 0 : READ_U64( bits_len, payload, payload_sz );
462 0 : CHECK( !bits_len );
463 0 : return 1;
464 0 : }
465 :
466 0 : ulong bits_cap;
467 0 : READ_U64( bits_cap, payload, payload_sz );
468 0 : SKIP_BYTES( bits_cap, payload, payload_sz );
469 0 : ulong bits_len;
470 0 : READ_U64( bits_len, payload, payload_sz );
471 0 : CHECK( bits_len<=bits_cap*8UL );
472 0 : return 1;
473 0 : }
474 :
475 : static int
476 : deser_restart_last_voted_fork_slots( fd_gossip_value_t * value,
477 : uchar const ** payload,
478 0 : ulong * payload_sz ) {
479 0 : READ_BYTES( value->origin, 32UL, payload, payload_sz );
480 0 : READ_WALLCLOCK( value->wallclock, payload, payload_sz );
481 0 : uint is_raw_offsets;
482 0 : READ_ENUM( is_raw_offsets, 2UL, payload, payload_sz );
483 0 : if( FD_LIKELY( is_raw_offsets ) ) {
484 0 : CHECK( deser_bitvec_u8_restart_last_voted_fork_slots( payload, payload_sz ) );
485 0 : } else {
486 0 : ulong slots_len;
487 0 : READ_U64( slots_len, payload, payload_sz );
488 0 : for( ulong i=0UL; i<slots_len; i++ ) {
489 0 : ushort _slot;
490 0 : READ_U16_VARINT( _slot, payload, payload_sz );
491 0 : (void)_slot;
492 0 : }
493 0 : }
494 0 : SKIP_BYTES( 8UL+32UL+2UL, payload, payload_sz ); /* last voted slot + last voted hash + shred version */
495 0 : return 1;
496 0 : }
497 :
498 : static int
499 : deser_restart_heaviest_fork( fd_gossip_value_t * value,
500 : uchar const ** payload,
501 0 : ulong * payload_sz ) {
502 0 : READ_BYTES( value->origin, 32UL, payload, payload_sz );
503 0 : READ_WALLCLOCK( value->wallclock, payload, payload_sz );
504 0 : SKIP_BYTES( 8UL+32UL+8UL+2UL, payload, payload_sz ); /* last slot + last slot hash + observed stake + shred version */
505 0 : return 1;
506 0 : }
507 :
508 : static int
509 : deser_value( fd_gossip_value_t * value,
510 : uchar const ** payload,
511 33 : ulong * payload_sz ) {
512 33 : READ_BYTES( value->signature, 64UL, payload, payload_sz );
513 33 : READ_ENUM( value->tag, FD_GOSSIP_VALUE_CNT, payload, payload_sz );
514 :
515 33 : switch( value->tag ) {
516 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 */
517 0 : case FD_GOSSIP_VALUE_VOTE: return deser_vote( value, payload, payload_sz );
518 0 : case FD_GOSSIP_VALUE_LOWEST_SLOT: return deser_lowest_slot( value, payload, payload_sz );
519 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 */
520 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 */
521 21 : case FD_GOSSIP_VALUE_EPOCH_SLOTS: return deser_epoch_slots( value, payload, payload_sz );
522 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 */
523 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 */
524 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 */
525 0 : case FD_GOSSIP_VALUE_DUPLICATE_SHRED: return deser_duplicate_shred( value, payload, payload_sz );
526 0 : case FD_GOSSIP_VALUE_SNAPSHOT_HASHES: return deser_snapshot_hashes( value, payload, payload_sz );
527 12 : case FD_GOSSIP_VALUE_CONTACT_INFO: return deser_contact_info( value, payload, payload_sz );
528 0 : case FD_GOSSIP_VALUE_RESTART_LAST_VOTED_FORK_SLOTS: return deser_restart_last_voted_fork_slots( value, payload, payload_sz );
529 0 : case FD_GOSSIP_VALUE_RESTART_HEAVIEST_FORK: return deser_restart_heaviest_fork( value, payload, payload_sz );
530 0 : default: FD_LOG_CRIT(( "impossible" ));
531 33 : }
532 33 : }
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 21 : ulong * payload_sz ) {
539 21 : uchar has_bits;
540 21 : READ_OPTION( has_bits, payload, payload_sz );
541 21 : if( FD_UNLIKELY( !has_bits ) ) {
542 6 : bloom->bits_cap = 0UL;
543 6 : READ_U64( bloom->bits_len, payload, payload_sz );
544 6 : CHECK( !bloom->bits_len );
545 3 : return 1;
546 6 : }
547 :
548 15 : READ_U64( bloom->bits_cap, payload, payload_sz );
549 15 : ulong bits_sz;
550 15 : CHECK( !__builtin_mul_overflow( bloom->bits_cap, 8UL, &bits_sz ) );
551 15 : READ_BYTES( bloom->bits, bits_sz, payload, payload_sz );
552 15 : READ_U64( bloom->bits_len, payload, payload_sz );
553 15 : CHECK( bloom->bits_len<=bits_sz*8UL );
554 9 : return 1;
555 15 : }
556 :
557 : static int
558 : deser_pull_request( fd_gossip_message_t * message,
559 : uchar const ** payload,
560 : ulong * payload_sz,
561 21 : ulong original_sz ) {
562 21 : READ_U64( message->pull_request->crds_filter->filter->keys_len, payload, payload_sz );
563 21 : 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 21 : CHECK( deser_bitvec_u64( message->pull_request->crds_filter->filter, payload, payload_sz ) );
568 :
569 12 : READ_U64( message->pull_request->crds_filter->filter->num_bits_set, payload, payload_sz );
570 12 : READ_U64( message->pull_request->crds_filter->mask, payload, payload_sz );
571 12 : 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 12 : CHECK( message->pull_request->crds_filter->mask_bits>=FD_GOSSIP_MIN_PULL_REQUEST_MASK_BITS );
575 :
576 12 : message->pull_request->contact_info->offset = original_sz-*payload_sz;
577 12 : CHECK( deser_value( message->pull_request->contact_info, payload, payload_sz ) );
578 12 : 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 12 : CHECK( message->pull_request->contact_info->tag==FD_GOSSIP_VALUE_CONTACT_INFO );
581 12 : return 1;
582 12 : }
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 21 : ulong original_sz ) {
604 21 : READ_BYTES( message->push->from, 32UL, payload, payload_sz );
605 21 : READ_U64( message->push->values_len, payload, payload_sz );
606 30 : for( ulong i=0UL; i<message->push->values_len; i++ ) {
607 21 : message->push->values[ i ].offset = original_sz-*payload_sz;
608 21 : CHECK( deser_value( &message->push->values[ i ], payload, payload_sz ) );
609 9 : message->push->values[ i ].length = original_sz-*payload_sz-message->push->values[ i ].offset;
610 9 : }
611 9 : return 1;
612 21 : }
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 42 : ulong _payload_sz ) {
655 42 : uchar const ** payload = &_payload;
656 42 : ulong * payload_sz = &_payload_sz;
657 42 : ulong original_sz = _payload_sz;
658 :
659 42 : CHECK( _payload_sz<=1232UL );
660 42 : READ_ENUM( message->tag, FD_GOSSIP_MESSAGE_CNT, payload, payload_sz );
661 :
662 42 : switch( message->tag ){
663 21 : case FD_GOSSIP_MESSAGE_PULL_REQUEST: CHECK( deser_pull_request( message, payload, payload_sz, original_sz ) ); break;
664 12 : case FD_GOSSIP_MESSAGE_PULL_RESPONSE: CHECK( deser_pull_response( message, payload, payload_sz, original_sz ) ); break;
665 21 : case FD_GOSSIP_MESSAGE_PUSH: CHECK( deser_push( message, payload, payload_sz, original_sz ) ); break;
666 9 : 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 42 : }
671 :
672 21 : return !*payload_sz;
673 42 : }
674 :
675 33 : #define CHECK1( cond ) do { \
676 33 : if( FD_UNLIKELY( !(cond) ) ) return -1; \
677 33 : } while( 0 )
678 :
679 3 : #define WRITE_BYTES( src, src_sz, out, out_sz ) do { \
680 3 : CHECK1( *out_sz>=src_sz ); \
681 3 : fd_memcpy( *out, src, src_sz ); \
682 3 : (*out) += src_sz; \
683 3 : (*out_sz) -= src_sz; \
684 3 : } while( 0 )
685 :
686 9 : #define WRITE_SKIP_BYTES( skip_sz, out, out_sz ) do { \
687 9 : CHECK1( *out_sz>=skip_sz ); \
688 9 : (*out) += skip_sz; \
689 9 : (*out_sz) -= skip_sz; \
690 9 : } while( 0 )
691 :
692 3 : #define WRITE_U8( val, out, out_sz ) do { \
693 3 : CHECK1( *out_sz>=1UL ); \
694 3 : FD_STORE( uchar, *out, val ); \
695 3 : (*out) += 1UL; \
696 3 : (*out_sz) -= 1UL; \
697 3 : } 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 6 : #define WRITE_U32( val, out, out_sz ) do { \
707 6 : CHECK1( *out_sz>=4UL ); \
708 6 : FD_STORE( uint, *out, val ); \
709 6 : (*out) += 4UL; \
710 6 : (*out_sz) -= 4UL; \
711 6 : } while( 0 )
712 :
713 12 : #define WRITE_U64( val, out, out_sz ) do { \
714 12 : CHECK1( *out_sz>=8UL ); \
715 12 : FD_STORE( ulong, *out, val ); \
716 12 : (*out) += 8UL; \
717 12 : (*out_sz) -= 8UL; \
718 12 : } 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 3 : uchar ** out_bits_set ) {
924 3 : uchar ** out = &payload;
925 3 : ulong original_size = payload_sz;
926 3 : ulong * out_sz = &payload_sz;
927 :
928 3 : WRITE_U32( FD_GOSSIP_MESSAGE_PULL_REQUEST, out, out_sz );
929 3 : WRITE_U64( num_keys, out, out_sz );
930 3 : *out_bloom_keys = payload+(payload_sz-*out_sz);
931 3 : WRITE_SKIP_BYTES( num_keys*8UL, out, out_sz );
932 :
933 3 : 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 3 : ulong bloom_vec_len = (num_bits+63UL)/64UL;
936 3 : WRITE_U8( 1, out, out_sz ); /* has_bits */
937 3 : WRITE_U64( bloom_vec_len, out, out_sz );
938 3 : *out_bloom_bits = payload+(payload_sz-*out_sz);
939 3 : WRITE_SKIP_BYTES( bloom_vec_len*8UL, out, out_sz );
940 3 : } else {
941 0 : WRITE_U8( 0, out, out_sz ); /* has_bits */
942 0 : *out_bloom_bits = NULL;
943 0 : }
944 3 : WRITE_U64( num_bits, out, out_sz );
945 3 : *out_bits_set = payload+(payload_sz-*out_sz);
946 3 : WRITE_SKIP_BYTES( 8UL, out, out_sz );
947 3 : WRITE_U64( mask, out, out_sz );
948 3 : WRITE_U32( mask_bits, out, out_sz );
949 3 : WRITE_BYTES( contact_info_crds, contact_info_crds_sz, out, out_sz );
950 :
951 3 : return (long)(original_size-*out_sz);
952 3 : }
|