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