Line data Source code
1 : #define _GNU_SOURCE 1
2 : #include "fd_gossip.h"
3 : #include "../../ballet/sha256/fd_sha256.h"
4 : #include "../../ballet/ed25519/fd_ed25519.h"
5 : #include "../../ballet/base58/fd_base58.h"
6 : #include "../../disco/keyguard/fd_keyguard.h"
7 : #include "../../util/net/fd_eth.h"
8 : #include "../../util/rng/fd_rng.h"
9 : #include <string.h>
10 : #include <stdio.h>
11 : #include <math.h>
12 : #include <sys/types.h>
13 : #include <sys/socket.h>
14 : #include <arpa/inet.h>
15 : #include <netinet/in.h>
16 : #include <sys/random.h>
17 :
18 : #pragma GCC diagnostic ignored "-Wstrict-aliasing"
19 :
20 : /* Maximum size of a network packet */
21 0 : #define PACKET_DATA_SIZE 1232
22 : /* How long do we remember values (in millisecs) */
23 0 : #define FD_GOSSIP_VALUE_EXPIRE ((ulong)(60e3)) /* 1 minute */
24 : /* Max age that values can be pushed/pulled (in millisecs) */
25 0 : #define FD_GOSSIP_PULL_TIMEOUT ((ulong)(15e3)) /* 15 seconds */
26 : /* Max number of validators that can be actively pinged */
27 0 : #define FD_ACTIVE_KEY_MAX (1<<8)
28 : /* Max number of values that can be remembered */
29 0 : #define FD_VALUE_KEY_MAX (1<<16)
30 : /* Max number of pending timed events */
31 0 : #define FD_PENDING_MAX (1<<9)
32 : /* Number of bloom filter bits in an outgoing pull request packet */
33 0 : #define FD_BLOOM_NUM_BITS (512U*8U) /* 0.5 Kbyte */
34 : /* Max number of bloom filter keys in an outgoing pull request packet */
35 0 : #define FD_BLOOM_MAX_KEYS 32U
36 : /* Max number of packets in an outgoing pull request batch */
37 0 : #define FD_BLOOM_MAX_PACKETS 32U
38 : /* Number of bloom bits in a push prune filter */
39 0 : #define FD_PRUNE_NUM_BITS (512U*8U) /* 0.5 Kbyte */
40 : /* Number of bloom keys in a push prune filter */
41 0 : #define FD_PRUNE_NUM_KEYS 4U
42 : /* Max number of destinations a single message can be pushed */
43 0 : #define FD_PUSH_VALUE_MAX 9
44 : /* Max number of push destinations that we track */
45 0 : #define FD_PUSH_LIST_MAX 12
46 : /* Max length of queue of values that need pushing */
47 0 : #define FD_NEED_PUSH_MAX (1<<12)
48 : /* Max size of receive statistics table */
49 0 : #define FD_STATS_KEY_MAX (1<<8)
50 : /* Sha256 pre-image size for pings/pongs */
51 0 : #define FD_PING_PRE_IMAGE_SZ (48UL)
52 : /* Number of recognized CRDS enum members */
53 0 : #define FD_KNOWN_CRDS_ENUM_MAX (14UL)
54 : /* Prune data prefix
55 : https://github.com/anza-xyz/agave/blob/0c264859b127940f13673b5fea300131a70b1a8d/gossip/src/protocol.rs#L39 */
56 0 : #define FD_GOSSIP_PRUNE_DATA_PREFIX "\xffSOLANA_PRUNE_DATA"
57 :
58 0 : #define FD_NANOSEC_TO_MILLI(_ts_) ((ulong)(_ts_/1000000))
59 :
60 : /* Maximum number of stake weights, mirrors fd_stake_ci */
61 0 : #define MAX_STAKE_WEIGHTS (40200UL)
62 :
63 0 : #define MAX_PEER_PING_COUNT (10000U)
64 :
65 : /* Test if two addresses are equal */
66 0 : static int fd_gossip_peer_addr_eq( const fd_gossip_peer_addr_t * key1, const fd_gossip_peer_addr_t * key2 ) {
67 0 : FD_STATIC_ASSERT(sizeof(fd_gossip_peer_addr_t) == sizeof(ulong),"messed up size");
68 0 : return key1->l == key2->l;
69 0 : }
70 :
71 : /* Hash an address */
72 0 : static ulong fd_gossip_peer_addr_hash( const fd_gossip_peer_addr_t * key, ulong seed ) {
73 0 : FD_STATIC_ASSERT(sizeof(fd_gossip_peer_addr_t) == sizeof(ulong),"messed up size");
74 0 : return (key->l + seed + 7242237688154252699UL)*9540121337UL;
75 0 : }
76 :
77 : /* Efficiently copy an address */
78 0 : static void fd_gossip_peer_addr_copy( fd_gossip_peer_addr_t * keyd, const fd_gossip_peer_addr_t * keys ) {
79 0 : FD_STATIC_ASSERT(sizeof(fd_gossip_peer_addr_t) == sizeof(ulong),"messed up size");
80 0 : keyd->l = keys->l;
81 0 : }
82 :
83 : /* All peers table element. The peers table is all known validator addresses/ids. */
84 : struct fd_peer_elem {
85 : fd_gossip_peer_addr_t key;
86 : ulong next;
87 : fd_pubkey_t id; /* Public indentifier */
88 : ulong wallclock; /* last time we heard about this peer */
89 : ulong stake; /* Staking for this validator. Unimplemented. */
90 : };
91 : /* All peers table */
92 : typedef struct fd_peer_elem fd_peer_elem_t;
93 : #define MAP_NAME fd_peer_table
94 : #define MAP_KEY_T fd_gossip_peer_addr_t
95 0 : #define MAP_KEY_EQ fd_gossip_peer_addr_eq
96 0 : #define MAP_KEY_HASH fd_gossip_peer_addr_hash
97 0 : #define MAP_KEY_COPY fd_gossip_peer_addr_copy
98 0 : #define MAP_T fd_peer_elem_t
99 : #include "../../util/tmpl/fd_map_giant.c"
100 :
101 : /* Active table element. This table is all validators that we are
102 : aggressively pinging for liveness checking. */
103 : struct fd_active_elem {
104 : fd_gossip_peer_addr_t key;
105 : ulong next;
106 : fd_pubkey_t id; /* Public indentifier */
107 : long pingtime; /* Last time we sent a ping */
108 : uint pingcount; /* Number of pings it took to get a pong */
109 : fd_hash_t pingtoken; /* Random data used in ping/pong */
110 : long pongtime; /* Last time we received a pong */
111 : ulong weight; /* Selection weight */
112 : };
113 : /* Active table */
114 : typedef struct fd_active_elem fd_active_elem_t;
115 : #define MAP_NAME fd_active_table
116 : #define MAP_KEY_T fd_gossip_peer_addr_t
117 0 : #define MAP_KEY_EQ fd_gossip_peer_addr_eq
118 0 : #define MAP_KEY_HASH fd_gossip_peer_addr_hash
119 0 : #define MAP_KEY_COPY fd_gossip_peer_addr_copy
120 0 : #define MAP_T fd_active_elem_t
121 : #include "../../util/tmpl/fd_map_giant.c"
122 :
123 : /* Initialize an active table element value */
124 : void
125 0 : fd_active_new_value(fd_active_elem_t * val) {
126 0 : val->pingcount = 1;
127 0 : val->pingtime = val->pongtime = 0;
128 0 : val->weight = 0;
129 0 : fd_memset(val->id.uc, 0, 32U);
130 0 : fd_memset(val->pingtoken.uc, 0, 32U);
131 0 : }
132 :
133 : /* Test if two hash values are equal */
134 0 : int fd_hash_eq( const fd_hash_t * key1, const fd_hash_t * key2 ) {
135 0 : for (ulong i = 0; i < 32U/sizeof(ulong); ++i)
136 0 : if (key1->ul[i] != key2->ul[i])
137 0 : return 0;
138 0 : return 1;
139 0 : }
140 :
141 : /* Hash a hash value */
142 0 : ulong fd_hash_hash( const fd_hash_t * key, ulong seed ) {
143 0 : return key->ul[0] ^ seed;
144 0 : }
145 :
146 : /* Copy a hash value */
147 0 : void fd_hash_copy( fd_hash_t * keyd, const fd_hash_t * keys ) {
148 0 : for (ulong i = 0; i < 32U/sizeof(ulong); ++i)
149 0 : keyd->ul[i] = keys->ul[i];
150 0 : }
151 :
152 : /* Value table element. This table stores all received crds
153 : values. Keyed by the hash of the value data. */
154 : struct fd_value_elem {
155 : fd_hash_t key;
156 : ulong next;
157 : fd_pubkey_t origin; /* Where did this value originate */
158 : ulong wallclock; /* Original timestamp of value in millis */
159 : uchar data[PACKET_DATA_SIZE]; /* Serialized form of value (bincode) including signature */
160 : ulong datalen;
161 : };
162 : /* Value table */
163 : typedef struct fd_value_elem fd_value_elem_t;
164 : #define MAP_NAME fd_value_table
165 : #define MAP_KEY_T fd_hash_t
166 0 : #define MAP_KEY_EQ fd_hash_eq
167 0 : #define MAP_KEY_HASH fd_hash_hash
168 0 : #define MAP_KEY_COPY fd_hash_copy
169 0 : #define MAP_T fd_value_elem_t
170 : #include "../../util/tmpl/fd_map_giant.c"
171 :
172 : /* Weights table element. This table stores the weight for each peer
173 : (determined by stake). */
174 : struct fd_weights_elem {
175 : fd_pubkey_t key;
176 : ulong next;
177 : ulong weight;
178 : };
179 : /* Weights table */
180 : typedef struct fd_weights_elem fd_weights_elem_t;
181 : #define MAP_NAME fd_weights_table
182 : #define MAP_KEY_T fd_hash_t
183 0 : #define MAP_KEY_EQ fd_hash_eq
184 0 : #define MAP_KEY_HASH fd_hash_hash
185 0 : #define MAP_KEY_COPY fd_hash_copy
186 0 : #define MAP_T fd_weights_elem_t
187 : #include "../../util/tmpl/fd_map_giant.c"
188 :
189 : /* Queue of pending timed events, stored as a priority heap */
190 : union fd_pending_event_arg {
191 : fd_gossip_peer_addr_t key;
192 : };
193 : typedef union fd_pending_event_arg fd_pending_event_arg_t;
194 : typedef void (*fd_pending_event_fun)(struct fd_gossip * glob, fd_pending_event_arg_t * arg);
195 : struct fd_pending_event {
196 : ulong left;
197 : ulong right;
198 : long key;
199 : fd_pending_event_fun fun;
200 : fd_pending_event_arg_t fun_arg;
201 : };
202 : typedef struct fd_pending_event fd_pending_event_t;
203 : #define POOL_NAME fd_pending_pool
204 0 : #define POOL_T fd_pending_event_t
205 0 : #define POOL_NEXT left
206 : #include "../../util/tmpl/fd_pool.c"
207 : #define HEAP_NAME fd_pending_heap
208 : #define HEAP_T fd_pending_event_t
209 0 : #define HEAP_LT(e0,e1) (e0->key < e1->key)
210 : #include "../../util/tmpl/fd_heap.c"
211 :
212 : /* Data structure representing an active push destination. There are
213 : only a small number of these. */
214 : struct fd_push_state {
215 : fd_gossip_peer_addr_t addr; /* Destination address */
216 : fd_pubkey_t id; /* Public indentifier */
217 : ulong drop_cnt; /* Number of values dropped due to pruning */
218 : ulong prune_keys[FD_PRUNE_NUM_KEYS]; /* Keys used for bloom filter for pruning */
219 : ulong prune_bits[FD_PRUNE_NUM_BITS/64U]; /* Bits table used for bloom filter for pruning */
220 : uchar packet[PACKET_DATA_SIZE]; /* Partially assembled packet containing a fd_gossip_push_msg_t */
221 : uchar * packet_end_init; /* Initial end of the packet when there are zero values */
222 : uchar * packet_end; /* Current end of the packet including values so far */
223 : ulong next;
224 : };
225 : typedef struct fd_push_state fd_push_state_t;
226 :
227 : #define POOL_NAME fd_push_states_pool
228 0 : #define POOL_T fd_push_state_t
229 : #include "../../util/tmpl/fd_pool.c"
230 :
231 : /* Receive statistics table element. */
232 : struct fd_stats_elem {
233 : fd_gossip_peer_addr_t key; /* Keyed by sender */
234 : ulong next;
235 : long last; /* Timestamp of last update */
236 : /* Duplicate counts by origin */
237 : struct {
238 : fd_pubkey_t origin;
239 : ulong cnt;
240 : } dups[8];
241 : ulong dups_cnt;
242 : };
243 : /* Receive statistics table. */
244 : typedef struct fd_stats_elem fd_stats_elem_t;
245 : #define MAP_NAME fd_stats_table
246 : #define MAP_KEY_T fd_gossip_peer_addr_t
247 0 : #define MAP_KEY_EQ fd_gossip_peer_addr_eq
248 0 : #define MAP_KEY_HASH fd_gossip_peer_addr_hash
249 0 : #define MAP_KEY_COPY fd_gossip_peer_addr_copy
250 0 : #define MAP_T fd_stats_elem_t
251 : #include "../../util/tmpl/fd_map_giant.c"
252 :
253 : struct fd_msg_stats_elem {
254 : ulong bytes_rx_cnt;
255 : ulong total_cnt;
256 : ulong dups_cnt;
257 : };
258 : /* Receive type statistics table. */
259 : typedef struct fd_msg_stats_elem fd_msg_stats_elem_t;
260 :
261 : /* Global data for gossip service */
262 : struct fd_gossip {
263 : /* Concurrency lock */
264 : volatile ulong lock;
265 : /* Current time in nanosecs */
266 : long now;
267 : /* My public/private key */
268 : fd_pubkey_t * public_key;
269 : uchar * private_key;
270 : /* My gossip port address */
271 : fd_gossip_peer_addr_t my_addr;
272 : /* My official contact info in the gossip protocol */
273 : fd_gossip_contact_info_v1_t my_contact_info;
274 : fd_gossip_version_v2_t my_version;
275 : /* Function used to deliver gossip messages to the application */
276 : fd_gossip_data_deliver_fun deliver_fun;
277 : /* Argument to fd_gossip_data_deliver_fun */
278 : void * deliver_arg;
279 : /* Function used to send raw packets on the network */
280 : fd_gossip_send_packet_fun send_fun;
281 : /* Argument to fd_gossip_send_packet_fun */
282 : void * send_arg;
283 : /* Function used to send packets for signing to remote tile */
284 : fd_gossip_sign_fun sign_fun;
285 : /* Argument to fd_gossip_sign_fun */
286 : void * sign_arg;
287 : /* Table of all known validators, keyed by gossip address */
288 : fd_peer_elem_t * peers;
289 : /* Table of validators that we are actively pinging, keyed by gossip address */
290 : fd_active_elem_t * actives;
291 : /* Queue of validators that might be added to actives */
292 : fd_gossip_peer_addr_t * inactives;
293 : ulong inactives_cnt;
294 0 : #define INACTIVES_MAX 1024U
295 : /* Table of crds values that we have received in the last 5 minutes, keys by hash */
296 : fd_value_elem_t * values;
297 : /* The last timestamp hash that we pushed our own contact info */
298 : long last_contact_time;
299 : fd_hash_t last_contact_info_key;
300 : fd_hash_t last_version_key;
301 : fd_hash_t last_contact_info_v2_key;
302 : fd_hash_t last_node_instance_key;
303 :
304 : /* Array of push destinations currently in use */
305 : fd_push_state_t * push_states[FD_PUSH_LIST_MAX];
306 : ulong push_states_cnt;
307 : fd_push_state_t * push_states_pool;
308 : /* Queue of values that need pushing */
309 : fd_hash_t * need_push;
310 : ulong need_push_head;
311 : ulong need_push_cnt;
312 : /* Table of receive statistics */
313 : fd_stats_elem_t * stats;
314 : /* Table of message type stats */
315 : fd_msg_stats_elem_t msg_stats[ FD_KNOWN_CRDS_ENUM_MAX ];
316 : /* Heap/queue of pending timed events */
317 : fd_pending_event_t * event_pool;
318 : fd_pending_heap_t * event_heap;
319 : /* Random number generator */
320 : fd_rng_t rng[1];
321 : /* RNG seed */
322 : ulong seed;
323 : /* Total number of packeets received */
324 : ulong recv_pkt_cnt;
325 : /* Total number of duplicate values received */
326 : ulong recv_dup_cnt;
327 : /* Total number of non-duplicate values received */
328 : ulong recv_nondup_cnt;
329 : /* Count of values pushed */
330 : ulong push_cnt;
331 : /* Count of values not pushed due to pruning */
332 : ulong not_push_cnt;
333 : /* Stake weights */
334 : fd_weights_elem_t * weights;
335 : /* List of added entrypoints at startup */
336 : ulong entrypoints_cnt;
337 : fd_gossip_peer_addr_t entrypoints[16];
338 : };
339 :
340 : ulong
341 0 : fd_gossip_align ( void ) { return 128UL; }
342 :
343 : ulong
344 0 : fd_gossip_footprint( void ) {
345 0 : ulong l = FD_LAYOUT_INIT;
346 0 : l = FD_LAYOUT_APPEND( l, alignof(fd_gossip_t), sizeof(fd_gossip_t) );
347 0 : l = FD_LAYOUT_APPEND( l, fd_peer_table_align(), fd_peer_table_footprint(FD_PEER_KEY_MAX) );
348 0 : l = FD_LAYOUT_APPEND( l, fd_active_table_align(), fd_active_table_footprint(FD_ACTIVE_KEY_MAX) );
349 0 : l = FD_LAYOUT_APPEND( l, alignof(fd_gossip_peer_addr_t), INACTIVES_MAX*sizeof(fd_gossip_peer_addr_t) );
350 0 : l = FD_LAYOUT_APPEND( l, alignof(fd_hash_t), FD_NEED_PUSH_MAX*sizeof(fd_hash_t) );
351 0 : l = FD_LAYOUT_APPEND( l, fd_value_table_align(), fd_value_table_footprint(FD_VALUE_KEY_MAX) );
352 0 : l = FD_LAYOUT_APPEND( l, fd_pending_pool_align(), fd_pending_pool_footprint(FD_PENDING_MAX) );
353 0 : l = FD_LAYOUT_APPEND( l, fd_pending_heap_align(), fd_pending_heap_footprint(FD_PENDING_MAX) );
354 0 : l = FD_LAYOUT_APPEND( l, fd_stats_table_align(), fd_stats_table_footprint(FD_STATS_KEY_MAX) );
355 0 : l = FD_LAYOUT_APPEND( l, fd_weights_table_align(), fd_weights_table_footprint(MAX_STAKE_WEIGHTS) );
356 0 : l = FD_LAYOUT_APPEND( l, fd_push_states_pool_align(), fd_push_states_pool_footprint(FD_PUSH_LIST_MAX) );
357 0 : l = FD_LAYOUT_FINI( l, fd_gossip_align() );
358 0 : return l;
359 0 : }
360 :
361 : void *
362 0 : fd_gossip_new ( void * shmem, ulong seed ) {
363 0 : FD_SCRATCH_ALLOC_INIT(l, shmem);
364 0 : fd_gossip_t * glob = (fd_gossip_t*)FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_gossip_t), sizeof(fd_gossip_t)) ;
365 0 : fd_memset(glob, 0, sizeof(fd_gossip_t));
366 0 : glob->seed = seed;
367 :
368 0 : void * shm = FD_SCRATCH_ALLOC_APPEND(l, fd_peer_table_align(), fd_peer_table_footprint(FD_PEER_KEY_MAX));
369 0 : glob->peers = fd_peer_table_join(fd_peer_table_new(shm, FD_PEER_KEY_MAX, seed));
370 :
371 0 : shm = FD_SCRATCH_ALLOC_APPEND(l, fd_active_table_align(), fd_active_table_footprint(FD_ACTIVE_KEY_MAX));
372 0 : glob->actives = fd_active_table_join(fd_active_table_new(shm, FD_ACTIVE_KEY_MAX, seed));
373 :
374 0 : glob->inactives = (fd_gossip_peer_addr_t*)FD_SCRATCH_ALLOC_APPEND(l, alignof(fd_gossip_peer_addr_t), INACTIVES_MAX*sizeof(fd_gossip_peer_addr_t));
375 0 : glob->need_push = (fd_hash_t*)FD_SCRATCH_ALLOC_APPEND(l, alignof(fd_hash_t), FD_NEED_PUSH_MAX*sizeof(fd_hash_t));
376 :
377 0 : shm = FD_SCRATCH_ALLOC_APPEND(l, fd_value_table_align(), fd_value_table_footprint(FD_VALUE_KEY_MAX));
378 0 : glob->values = fd_value_table_join(fd_value_table_new(shm, FD_VALUE_KEY_MAX, seed));
379 :
380 0 : glob->last_contact_time = 0;
381 0 : shm = FD_SCRATCH_ALLOC_APPEND(l, fd_pending_pool_align(), fd_pending_pool_footprint(FD_PENDING_MAX));
382 0 : glob->event_pool = fd_pending_pool_join(fd_pending_pool_new(shm, FD_PENDING_MAX));
383 :
384 0 : shm = FD_SCRATCH_ALLOC_APPEND(l, fd_pending_heap_align(), fd_pending_heap_footprint(FD_PENDING_MAX));
385 0 : glob->event_heap = fd_pending_heap_join(fd_pending_heap_new(shm, FD_PENDING_MAX));
386 :
387 0 : fd_rng_new(glob->rng, (uint)seed, 0UL);
388 0 : shm = FD_SCRATCH_ALLOC_APPEND(l, fd_stats_table_align(), fd_stats_table_footprint(FD_STATS_KEY_MAX));
389 0 : glob->stats = fd_stats_table_join(fd_stats_table_new(shm, FD_STATS_KEY_MAX, seed));
390 :
391 0 : shm = FD_SCRATCH_ALLOC_APPEND(l, fd_weights_table_align(), fd_weights_table_footprint(MAX_STAKE_WEIGHTS));
392 0 : glob->weights = fd_weights_table_join( fd_weights_table_new( shm, MAX_STAKE_WEIGHTS, seed ) );
393 :
394 0 : shm = FD_SCRATCH_ALLOC_APPEND(l, fd_push_states_pool_align(), fd_push_states_pool_footprint(FD_PUSH_LIST_MAX));
395 0 : glob->push_states_pool = fd_push_states_pool_join( fd_push_states_pool_new( shm, FD_PUSH_LIST_MAX ) );
396 :
397 0 : ulong scratch_top = FD_SCRATCH_ALLOC_FINI( l, fd_gossip_align() );
398 0 : if ( scratch_top > (ulong)shmem + fd_gossip_footprint() ) {
399 0 : FD_LOG_ERR(("Not enough space allocated for gossip"));
400 0 : }
401 0 : return glob;
402 0 : }
403 :
404 : fd_gossip_t *
405 0 : fd_gossip_join ( void * shmap ) { return (fd_gossip_t *)shmap; }
406 :
407 : void *
408 0 : fd_gossip_leave ( fd_gossip_t * join ) { return join; }
409 :
410 : void *
411 0 : fd_gossip_delete ( void * shmap ) {
412 0 : fd_gossip_t * glob = (fd_gossip_t *)shmap;
413 0 : fd_peer_table_delete( fd_peer_table_leave( glob->peers ) );
414 0 : fd_active_table_delete( fd_active_table_leave( glob->actives ) );
415 :
416 0 : fd_value_table_delete( fd_value_table_leave( glob->values ) );
417 0 : fd_pending_pool_delete( fd_pending_pool_leave( glob->event_pool ) );
418 0 : fd_pending_heap_delete( fd_pending_heap_leave( glob->event_heap ) );
419 0 : fd_stats_table_delete( fd_stats_table_leave( glob->stats ) );
420 0 : fd_weights_table_delete( fd_weights_table_leave( glob->weights ) );
421 0 : fd_push_states_pool_delete( fd_push_states_pool_leave( glob->push_states_pool ) );
422 :
423 0 : return glob;
424 0 : }
425 :
426 : static void
427 0 : fd_gossip_lock( fd_gossip_t * gossip ) {
428 0 : # if FD_HAS_THREADS
429 0 : for(;;) {
430 0 : if( FD_LIKELY( !FD_ATOMIC_CAS( &gossip->lock, 0UL, 1UL) ) ) break;
431 0 : FD_SPIN_PAUSE();
432 0 : }
433 : # else
434 : gossip->lock = 1;
435 : # endif
436 0 : FD_COMPILER_MFENCE();
437 0 : }
438 :
439 : static void
440 0 : fd_gossip_unlock( fd_gossip_t * gossip ) {
441 0 : FD_COMPILER_MFENCE();
442 0 : FD_VOLATILE( gossip->lock ) = 0UL;
443 0 : }
444 :
445 : /* FIXME: do these go in fd_types_custom instead? */
446 : void
447 0 : fd_gossip_ipaddr_from_socketaddr( fd_gossip_socket_addr_t const * addr, fd_gossip_ip_addr_t * out ) {
448 0 : if( FD_LIKELY( addr->discriminant == fd_gossip_socket_addr_enum_ip4 ) ) {
449 0 : fd_gossip_ip_addr_new_disc(out, fd_gossip_ip_addr_enum_ip4);
450 0 : out->inner.ip4 = addr->inner.ip4.addr;
451 0 : } else {
452 0 : fd_gossip_ip_addr_new_disc(out, fd_gossip_ip_addr_enum_ip6);
453 0 : out->inner.ip6 = addr->inner.ip6.addr;
454 0 : }
455 0 : }
456 :
457 : ushort
458 0 : fd_gossip_port_from_socketaddr( fd_gossip_socket_addr_t const * addr ) {
459 0 : if( FD_LIKELY( addr->discriminant == fd_gossip_socket_addr_enum_ip4 ) ) {
460 0 : return addr->inner.ip4.port;
461 0 : } else {
462 0 : return addr->inner.ip6.port;
463 0 : }
464 0 : }
465 :
466 :
467 : void
468 : fd_gossip_contact_info_v2_to_v1( fd_gossip_contact_info_v2_t const * v2,
469 0 : fd_gossip_contact_info_v1_t * v1 ) {
470 0 : memset( v1, 0, sizeof(fd_gossip_contact_info_v1_t) );
471 0 : v1->id = v2->from;
472 0 : v1->shred_version = v2->shred_version;
473 0 : v1->wallclock = v2->wallclock;
474 0 : fd_gossip_contact_info_v2_find_proto_ident( v2, FD_GOSSIP_SOCKET_TAG_GOSSIP, &v1->gossip );
475 0 : fd_gossip_contact_info_v2_find_proto_ident( v2, FD_GOSSIP_SOCKET_TAG_SERVE_REPAIR, &v1->serve_repair );
476 0 : fd_gossip_contact_info_v2_find_proto_ident( v2, FD_GOSSIP_SOCKET_TAG_TPU, &v1->tpu );
477 0 : fd_gossip_contact_info_v2_find_proto_ident( v2, FD_GOSSIP_SOCKET_TAG_TPU_VOTE, &v1->tpu_vote );
478 0 : fd_gossip_contact_info_v2_find_proto_ident( v2, FD_GOSSIP_SOCKET_TAG_TVU, &v1->tvu );
479 0 : }
480 :
481 : int
482 : fd_gossip_contact_info_v2_find_proto_ident( fd_gossip_contact_info_v2_t const * contact_info,
483 : uchar proto_ident,
484 0 : fd_gossip_socket_addr_t * out_addr ) {
485 0 : ushort port = 0;
486 0 : for( ulong i = 0UL; i<contact_info->sockets_len; i++ ) {
487 0 : fd_gossip_socket_entry_t const * socket_entry = &contact_info->sockets[ i ];
488 0 : port = (ushort)( port + socket_entry->offset );
489 0 : if( socket_entry->key==proto_ident ) {
490 0 : if( socket_entry->index>=contact_info->addrs_len) {
491 0 : continue;
492 0 : }
493 :
494 : /* Annoyingly, fd_gossip_socket_addr->inner and fd_gossip_ip_addr
495 : are slightly different, so we can't just
496 : out_addr->ip = contact_info->addrs[ idx ]
497 :
498 : Potential ptimization idea:
499 : - first 4 + 32/128 bytes of a fd_gossip_socket_addr_t can cast directly to fd_gossip_ip_addr_t AKA:
500 : fd_memcpy( out_addr, &contact_info->addrs[ socket_entry->index ], sizeof(fd_gossip_ip_addr_t) );
501 : out_addr->port = port; */
502 0 : fd_gossip_ip_addr_t * tmp = &contact_info->addrs[ socket_entry->index ];
503 0 : if( FD_LIKELY( tmp->discriminant == fd_gossip_ip_addr_enum_ip4 ) ) {
504 0 : out_addr->discriminant = fd_gossip_socket_addr_enum_ip4;
505 0 : out_addr->inner.ip4.addr = tmp->inner.ip4;
506 0 : out_addr->inner.ip4.port = port;
507 0 : } else {
508 0 : out_addr->discriminant = fd_gossip_socket_addr_enum_ip6;
509 0 : out_addr->inner.ip6.addr = tmp->inner.ip6;
510 0 : out_addr->inner.ip6.port = port;
511 0 : }
512 0 : return 1;
513 0 : }
514 0 : }
515 :
516 0 : return 0;
517 0 : }
518 :
519 : /* Convert my style of address to solana style */
520 : int
521 0 : fd_gossip_to_soladdr( fd_gossip_socket_addr_t * dst, fd_gossip_peer_addr_t const * src ) {
522 0 : fd_gossip_socket_addr_new_disc( dst, fd_gossip_socket_addr_enum_ip4 );
523 0 : dst->inner.ip4.port = ntohs(src->port);
524 0 : dst->inner.ip4.addr = src->addr;
525 0 : return 0;
526 0 : }
527 :
528 : /* Convert my style of address from solana style */
529 : int
530 0 : fd_gossip_from_soladdr(fd_gossip_peer_addr_t * dst, fd_gossip_socket_addr_t const * src ) {
531 0 : FD_STATIC_ASSERT(sizeof(fd_gossip_peer_addr_t) == sizeof(ulong),"messed up size");
532 0 : dst->l = 0;
533 0 : if (src->discriminant == fd_gossip_socket_addr_enum_ip4) {
534 0 : dst->port = htons(src->inner.ip4.port);
535 0 : dst->addr = src->inner.ip4.addr;
536 0 : return 0;
537 0 : } else {
538 0 : FD_LOG_ERR(("invalid address family %lu", (ulong)src->discriminant));
539 0 : return -1;
540 0 : }
541 0 : }
542 :
543 : /* Convert an address to a human readable string */
544 0 : const char * fd_gossip_addr_str( char * dst, size_t dstlen, fd_gossip_peer_addr_t const * src ) {
545 0 : char tmp[INET_ADDRSTRLEN];
546 0 : snprintf(dst, dstlen, "%s:%u", inet_ntop(AF_INET, &src->addr, tmp, INET_ADDRSTRLEN), (uint)ntohs(src->port));
547 0 : return dst;
548 0 : }
549 :
550 : /* Set the gossip configuration */
551 : int
552 0 : fd_gossip_set_config( fd_gossip_t * glob, const fd_gossip_config_t * config ) {
553 0 : fd_gossip_lock( glob );
554 :
555 0 : char tmp[100];
556 0 : char keystr[ FD_BASE58_ENCODED_32_SZ ];
557 0 : fd_base58_encode_32( config->public_key->uc, NULL, keystr );
558 0 : FD_LOG_NOTICE(("configuring address %s id %s", fd_gossip_addr_str(tmp, sizeof(tmp), &config->my_addr), keystr));
559 :
560 0 : glob->public_key = config->public_key;
561 0 : glob->private_key = config->private_key;
562 0 : fd_hash_copy(&glob->my_contact_info.id, config->public_key);
563 0 : fd_gossip_peer_addr_copy(&glob->my_addr, &config->my_addr);
564 0 : fd_gossip_to_soladdr(&glob->my_contact_info.gossip, &config->my_addr);
565 0 : glob->my_contact_info.shred_version = config->shred_version;
566 0 : glob->my_version = config->my_version;
567 0 : glob->deliver_fun = config->deliver_fun;
568 0 : glob->deliver_arg = config->deliver_arg;
569 0 : glob->send_fun = config->send_fun;
570 0 : glob->send_arg = config->send_arg;
571 0 : glob->sign_fun = config->sign_fun;
572 0 : glob->sign_arg = config->sign_arg;
573 :
574 0 : fd_gossip_unlock( glob );
575 :
576 0 : return 0;
577 0 : }
578 :
579 : int
580 0 : fd_gossip_update_addr( fd_gossip_t * glob, const fd_gossip_peer_addr_t * my_addr ) {
581 0 : char tmp[100];
582 0 : FD_LOG_NOTICE(("updating address %s", fd_gossip_addr_str(tmp, sizeof(tmp), my_addr)));
583 :
584 0 : fd_gossip_lock( glob );
585 0 : fd_gossip_peer_addr_copy(&glob->my_addr, my_addr);
586 0 : fd_gossip_to_soladdr(&glob->my_contact_info.gossip, my_addr);
587 0 : fd_gossip_unlock( glob );
588 0 : return 0;
589 0 : }
590 :
591 : int
592 0 : fd_gossip_update_repair_addr( fd_gossip_t * glob, const fd_gossip_peer_addr_t * serve ) {
593 0 : char tmp[100];
594 0 : FD_LOG_NOTICE(("updating repair service address %s", fd_gossip_addr_str(tmp, sizeof(tmp), serve)));
595 :
596 0 : fd_gossip_lock( glob );
597 0 : fd_gossip_to_soladdr(&glob->my_contact_info.serve_repair, serve);
598 0 : fd_gossip_unlock( glob );
599 0 : return 0;
600 0 : }
601 :
602 : int
603 0 : fd_gossip_update_tvu_addr( fd_gossip_t * glob, const fd_gossip_peer_addr_t * tvu, const fd_gossip_peer_addr_t * tvu_fwd ) {
604 0 : char tmp[100];
605 0 : FD_LOG_NOTICE(("updating tvu service address %s", fd_gossip_addr_str(tmp, sizeof(tmp), tvu)));
606 0 : FD_LOG_NOTICE(("updating tvu_fwd service address %s", fd_gossip_addr_str(tmp, sizeof(tmp), tvu_fwd)));
607 :
608 0 : fd_gossip_lock( glob );
609 0 : fd_gossip_to_soladdr(&glob->my_contact_info.tvu, tvu);
610 0 : fd_gossip_to_soladdr(&glob->my_contact_info.tvu_fwd, tvu_fwd);
611 0 : fd_gossip_unlock( glob );
612 0 : return 0;
613 0 : }
614 :
615 : int
616 : fd_gossip_update_tpu_addr( fd_gossip_t * glob,
617 : fd_gossip_peer_addr_t const * tpu,
618 0 : fd_gossip_peer_addr_t const * tpu_fwd ) {
619 0 : char tmp[100];
620 0 : FD_LOG_NOTICE(("updating tpu service address %s", fd_gossip_addr_str(tmp, sizeof(tmp), tpu) ));
621 0 : FD_LOG_NOTICE(("updating tpu_fwd service address %s", fd_gossip_addr_str(tmp, sizeof(tmp), tpu_fwd ) ));
622 :
623 0 : fd_gossip_lock( glob );
624 0 : fd_gossip_to_soladdr(&glob->my_contact_info.tpu, tpu);
625 0 : fd_gossip_to_soladdr(&glob->my_contact_info.tpu_fwd, tpu);
626 0 : fd_gossip_unlock( glob );
627 :
628 0 : return 0;
629 0 : }
630 :
631 : int
632 0 : fd_gossip_update_tpu_vote_addr( fd_gossip_t * glob, const fd_gossip_peer_addr_t * tpu_vote ) {
633 0 : char tmp[100];
634 0 : FD_LOG_NOTICE(("updating tpu vote service address %s", fd_gossip_addr_str(tmp, sizeof(tmp), tpu_vote)));
635 :
636 0 : fd_gossip_lock( glob );
637 0 : fd_gossip_to_soladdr(&glob->my_contact_info.tpu_vote, tpu_vote);
638 0 : fd_gossip_unlock( glob );
639 :
640 0 : return 0;
641 0 : }
642 :
643 : void
644 0 : fd_gossip_set_shred_version( fd_gossip_t * glob, ushort shred_version ) {
645 0 : glob->my_contact_info.shred_version = shred_version;
646 0 : }
647 :
648 : /* Add an event to the queue of pending timed events. The resulting
649 : value needs "fun" and "fun_arg" to be set. */
650 : static fd_pending_event_t *
651 0 : fd_gossip_add_pending( fd_gossip_t * glob, long when ) {
652 0 : if (fd_pending_pool_free( glob->event_pool ) == 0)
653 0 : return NULL;
654 0 : fd_pending_event_t * ev = fd_pending_pool_ele_acquire( glob->event_pool );
655 0 : ev->key = when;
656 0 : fd_pending_heap_ele_insert( glob->event_heap, ev, glob->event_pool );
657 0 : return ev;
658 0 : }
659 :
660 : /* Send raw data as a UDP packet to an address */
661 : static void
662 0 : fd_gossip_send_raw( fd_gossip_t * glob, const fd_gossip_peer_addr_t * dest, void * data, size_t sz) {
663 0 : if ( sz > PACKET_DATA_SIZE )
664 0 : FD_LOG_ERR(("sending oversized packet, size=%lu", sz));
665 0 : fd_gossip_unlock( glob );
666 0 : (*glob->send_fun)(data, sz, dest, glob->send_arg);
667 0 : fd_gossip_lock( glob );
668 0 : }
669 :
670 : /* Send a gossip message to an address */
671 : static void
672 0 : fd_gossip_send( fd_gossip_t * glob, const fd_gossip_peer_addr_t * dest, fd_gossip_msg_t * gmsg ) {
673 : /* Encode the data */
674 0 : uchar buf[PACKET_DATA_SIZE];
675 0 : fd_bincode_encode_ctx_t ctx;
676 0 : ctx.data = buf;
677 0 : ctx.dataend = buf + PACKET_DATA_SIZE;
678 0 : if ( fd_gossip_msg_encode( gmsg, &ctx ) ) {
679 0 : FD_LOG_WARNING(("fd_gossip_msg_encode failed"));
680 0 : return;
681 0 : }
682 0 : size_t sz = (size_t)((const uchar *)ctx.data - buf);
683 0 : fd_gossip_send_raw( glob, dest, buf, sz);
684 : // char tmp[100];
685 : // FD_LOG_WARNING(("sent msg type %u to %s size=%lu", gmsg->discriminant, fd_gossip_addr_str(tmp, sizeof(tmp), dest), sz));
686 0 : }
687 :
688 : /* Initiate the ping/pong protocol to a validator address */
689 : static void
690 0 : fd_gossip_make_ping( fd_gossip_t * glob, fd_pending_event_arg_t * arg ) {
691 : /* Update the active table where we track the state of the ping/pong
692 : protocol */
693 0 : fd_gossip_peer_addr_t * key = &arg->key;
694 0 : fd_active_elem_t * val = fd_active_table_query(glob->actives, key, NULL);
695 0 : if (val == NULL) {
696 0 : if (fd_active_table_is_full(glob->actives))
697 0 : return;
698 0 : val = fd_active_table_insert(glob->actives, key);
699 0 : fd_active_new_value(val);
700 0 : } else {
701 0 : if (val->pongtime != 0)
702 : /* Success */
703 0 : return;
704 0 : if (val->pingcount++ >= MAX_PEER_PING_COUNT) {
705 : /* Give up. This is a bad peer. */
706 0 : fd_active_table_remove(glob->actives, key);
707 0 : fd_peer_table_remove(glob->peers, key);
708 0 : return;
709 0 : }
710 0 : }
711 0 : val->pingtime = glob->now;
712 : /* Generate a new token when we start a fresh round of pinging */
713 0 : if (val->pingcount == 1U) {
714 0 : for ( ulong i = 0; i < FD_HASH_FOOTPRINT / sizeof(ulong); ++i )
715 0 : val->pingtoken.ul[i] = fd_rng_ulong(glob->rng);
716 0 : }
717 :
718 : /* Keep pinging until we succeed */
719 0 : fd_pending_event_t * ev = fd_gossip_add_pending( glob, glob->now + (long)2e8 /* 200 ms */ );
720 0 : if (ev != NULL) {
721 0 : ev->fun = fd_gossip_make_ping;
722 0 : fd_gossip_peer_addr_copy(&ev->fun_arg.key, key);
723 0 : }
724 :
725 0 : fd_pubkey_t * public_key = glob->public_key;
726 :
727 : /* Build a ping message */
728 0 : fd_gossip_msg_t gmsg;
729 0 : fd_gossip_msg_new_disc(&gmsg, fd_gossip_msg_enum_ping);
730 0 : fd_gossip_ping_t * ping = &gmsg.inner.ping;
731 0 : fd_hash_copy( &ping->from, public_key );
732 :
733 0 : uchar pre_image[FD_PING_PRE_IMAGE_SZ];
734 0 : fd_memcpy( pre_image, "SOLANA_PING_PONG", 16UL );
735 0 : fd_memcpy( pre_image+16UL, val->pingtoken.uc, 32UL );
736 :
737 0 : fd_sha256_hash( pre_image, FD_PING_PRE_IMAGE_SZ, &ping->token );
738 :
739 : /* Sign it */
740 :
741 0 : (*glob->sign_fun)( glob->sign_arg, ping->signature.uc, pre_image, FD_PING_PRE_IMAGE_SZ, FD_KEYGUARD_SIGN_TYPE_SHA256_ED25519 );
742 :
743 0 : fd_gossip_send( glob, key, &gmsg );
744 0 : }
745 :
746 : /* Respond to a ping from another validator */
747 : static void
748 0 : fd_gossip_handle_ping( fd_gossip_t * glob, const fd_gossip_peer_addr_t * from, fd_gossip_ping_t const * ping ) {
749 : /* Verify the signature */
750 0 : fd_sha512_t sha2[1];
751 0 : if (fd_ed25519_verify( /* msg */ ping->token.uc,
752 0 : /* sz */ 32UL,
753 0 : /* sig */ ping->signature.uc,
754 0 : /* public_key */ ping->from.uc,
755 0 : sha2 )) {
756 0 : FD_LOG_WARNING(("received ping with invalid signature"));
757 0 : return;
758 0 : }
759 :
760 : /* Build a pong message */
761 0 : fd_gossip_msg_t gmsg;
762 0 : fd_gossip_msg_new_disc(&gmsg, fd_gossip_msg_enum_pong);
763 0 : fd_gossip_ping_t * pong = &gmsg.inner.pong;
764 :
765 0 : fd_pubkey_t * public_key = glob->public_key;
766 :
767 0 : fd_hash_copy( &pong->from, public_key );
768 :
769 0 : uchar pre_image[FD_PING_PRE_IMAGE_SZ];
770 0 : fd_memcpy( pre_image, "SOLANA_PING_PONG", 16UL );
771 0 : fd_memcpy( pre_image+16UL, ping->token.uc, 32UL);
772 :
773 : /* Generate response hash token */
774 0 : fd_sha256_hash( pre_image, FD_PING_PRE_IMAGE_SZ, &pong->token );
775 :
776 : /* Sign it */
777 0 : (*glob->sign_fun)( glob->sign_arg, pong->signature.uc, pre_image, FD_PING_PRE_IMAGE_SZ, FD_KEYGUARD_SIGN_TYPE_SHA256_ED25519 );
778 :
779 0 : fd_gossip_send(glob, from, &gmsg);
780 0 : }
781 :
782 : /* Sign/timestamp an outgoing crds value */
783 : static void
784 0 : fd_gossip_sign_crds_value( fd_gossip_t * glob, fd_crds_value_t * crd ) {
785 : /* Update the identifier and timestamp */
786 0 : fd_pubkey_t * pubkey;
787 0 : ulong * wallclock;
788 0 : switch (crd->data.discriminant) {
789 0 : case fd_crds_data_enum_contact_info_v1:
790 0 : pubkey = &crd->data.inner.contact_info_v1.id;
791 0 : wallclock = &crd->data.inner.contact_info_v1.wallclock;
792 0 : break;
793 0 : case fd_crds_data_enum_vote:
794 0 : pubkey = &crd->data.inner.vote.from;
795 0 : wallclock = &crd->data.inner.vote.wallclock;
796 0 : break;
797 0 : case fd_crds_data_enum_lowest_slot:
798 0 : pubkey = &crd->data.inner.lowest_slot.from;
799 0 : wallclock = &crd->data.inner.lowest_slot.wallclock;
800 0 : break;
801 0 : case fd_crds_data_enum_snapshot_hashes:
802 0 : pubkey = &crd->data.inner.snapshot_hashes.from;
803 0 : wallclock = &crd->data.inner.snapshot_hashes.wallclock;
804 0 : break;
805 0 : case fd_crds_data_enum_accounts_hashes:
806 0 : pubkey = &crd->data.inner.accounts_hashes.from;
807 0 : wallclock = &crd->data.inner.accounts_hashes.wallclock;
808 0 : break;
809 0 : case fd_crds_data_enum_epoch_slots:
810 0 : pubkey = &crd->data.inner.epoch_slots.from;
811 0 : wallclock = &crd->data.inner.epoch_slots.wallclock;
812 0 : break;
813 0 : case fd_crds_data_enum_version_v1:
814 0 : pubkey = &crd->data.inner.version_v1.from;
815 0 : wallclock = &crd->data.inner.version_v1.wallclock;
816 0 : break;
817 0 : case fd_crds_data_enum_version_v2:
818 0 : pubkey = &crd->data.inner.version_v2.from;
819 0 : wallclock = &crd->data.inner.version_v2.wallclock;
820 0 : break;
821 0 : case fd_crds_data_enum_node_instance:
822 0 : pubkey = &crd->data.inner.node_instance.from;
823 0 : wallclock = &crd->data.inner.node_instance.wallclock;
824 0 : break;
825 0 : case fd_crds_data_enum_duplicate_shred:
826 0 : pubkey = &crd->data.inner.duplicate_shred.from;
827 0 : wallclock = &crd->data.inner.duplicate_shred.wallclock;
828 0 : break;
829 0 : case fd_crds_data_enum_incremental_snapshot_hashes:
830 0 : pubkey = &crd->data.inner.incremental_snapshot_hashes.from;
831 0 : wallclock = &crd->data.inner.incremental_snapshot_hashes.wallclock;
832 0 : break;
833 0 : case fd_crds_data_enum_contact_info_v2:
834 0 : pubkey = &crd->data.inner.contact_info_v2.from;
835 0 : wallclock = &crd->data.inner.contact_info_v2.wallclock;
836 0 : break;
837 0 : case fd_crds_data_enum_restart_last_voted_fork_slots:
838 0 : pubkey = &crd->data.inner.restart_last_voted_fork_slots.from;
839 0 : wallclock = &crd->data.inner.restart_last_voted_fork_slots.wallclock;
840 0 : break;
841 0 : case fd_crds_data_enum_restart_heaviest_fork:
842 0 : pubkey = &crd->data.inner.restart_heaviest_fork.from;
843 0 : wallclock = &crd->data.inner.restart_heaviest_fork.wallclock;
844 0 : break;
845 0 : default:
846 0 : return;
847 0 : }
848 0 : fd_pubkey_t * public_key = glob->public_key;
849 0 : fd_hash_copy(pubkey, public_key);
850 0 : *wallclock = FD_NANOSEC_TO_MILLI(glob->now); /* convert to ms */
851 :
852 : /* Sign it */
853 0 : uchar buf[PACKET_DATA_SIZE];
854 0 : fd_bincode_encode_ctx_t ctx;
855 0 : ctx.data = buf;
856 0 : ctx.dataend = buf + PACKET_DATA_SIZE;
857 0 : if ( fd_crds_data_encode( &crd->data, &ctx ) ) {
858 0 : FD_LOG_WARNING(("fd_crds_data_encode failed"));
859 0 : return;
860 0 : }
861 :
862 0 : (*glob->sign_fun)( glob->sign_arg, crd->signature.uc, buf, (ulong)((uchar*)ctx.data - buf), FD_KEYGUARD_SIGN_TYPE_ED25519 );
863 0 : }
864 :
865 : /* Convert a hash to a bloom filter bit position */
866 : static ulong
867 0 : fd_gossip_bloom_pos( fd_hash_t * hash, ulong key, ulong nbits) {
868 0 : for ( ulong i = 0; i < 32U; ++i) {
869 0 : key ^= (ulong)(hash->uc[i]);
870 0 : key *= 1099511628211UL;
871 0 : }
872 0 : return key % nbits;
873 0 : }
874 :
875 : /* Choose a random active peer with good ping count */
876 : static fd_active_elem_t *
877 0 : fd_gossip_random_active( fd_gossip_t * glob ) {
878 : /* Create a list of active peers with minimal pings */
879 0 : fd_active_elem_t * list[FD_ACTIVE_KEY_MAX];
880 0 : ulong listlen = 0;
881 0 : ulong totweight = 0;
882 0 : for( fd_active_table_iter_t iter = fd_active_table_iter_init( glob->actives );
883 0 : !fd_active_table_iter_done( glob->actives, iter );
884 0 : iter = fd_active_table_iter_next( glob->actives, iter ) ) {
885 0 : fd_active_elem_t * ele = fd_active_table_iter_ele( glob->actives, iter );
886 :
887 0 : if (ele->pongtime == 0 && !fd_gossip_is_allowed_entrypoint( glob, &ele->key )) {
888 0 : continue;
889 0 : } else if (listlen == 0) {
890 0 : list[0] = ele;
891 0 : listlen = 1;
892 0 : totweight = ele->weight;
893 0 : } else if (ele->pingcount > list[0]->pingcount) {
894 0 : continue;
895 0 : } else if (ele->pingcount < list[0]->pingcount) {
896 : /* Reset the list */
897 0 : list[0] = ele;
898 0 : listlen = 1;
899 0 : totweight = ele->weight;
900 0 : } else {
901 0 : list[listlen++] = ele;
902 0 : totweight += ele->weight;
903 0 : }
904 0 : }
905 0 : if (listlen == 0 || totweight == 0)
906 0 : return NULL;
907 : /* Choose a random list element by weight */
908 0 : ulong w = fd_rng_ulong(glob->rng) % totweight;
909 0 : ulong j = 0;
910 0 : for( ulong i = 0; i < listlen; ++i) {
911 0 : if( w < j + list[i]->weight )
912 0 : return list[i];
913 0 : j += list[i]->weight;
914 0 : }
915 0 : FD_LOG_CRIT(( "I shouldn't be here" ));
916 0 : return NULL;
917 0 : }
918 :
919 : /* Generate a pull request for a random active peer */
920 : static void
921 0 : fd_gossip_random_pull( fd_gossip_t * glob, fd_pending_event_arg_t * arg ) {
922 0 : (void)arg;
923 :
924 : /* Try again in 5 sec */
925 0 : fd_pending_event_t * ev = fd_gossip_add_pending(glob, glob->now + (long)100e6);
926 0 : if (ev) {
927 0 : ev->fun = fd_gossip_random_pull;
928 0 : }
929 :
930 : /* Pick a random partner */
931 0 : fd_active_elem_t * ele = fd_gossip_random_active(glob);
932 0 : if (ele == NULL)
933 0 : return;
934 :
935 : /* Compute the number of packets needed for all the bloom filter parts */
936 0 : ulong nitems = fd_value_table_key_cnt(glob->values);
937 0 : ulong nkeys = 1;
938 0 : ulong npackets = 1;
939 0 : uint nmaskbits = 0;
940 0 : double e = 0;
941 0 : if (nitems > 0) {
942 0 : do {
943 0 : double n = ((double)nitems)/((double)npackets); /* Assume even division of values */
944 0 : double m = (double)FD_BLOOM_NUM_BITS;
945 0 : nkeys = fd_ulong_max(1U, (ulong)((m/n)*0.69314718055994530941723212145818 /* ln(2) */));
946 0 : nkeys = fd_ulong_min(nkeys, FD_BLOOM_MAX_KEYS);
947 0 : if (npackets == FD_BLOOM_MAX_PACKETS)
948 0 : break;
949 0 : double k = (double)nkeys;
950 0 : e = pow(1.0 - exp(-k*n/m), k);
951 0 : if (e < 0.001)
952 0 : break;
953 0 : nmaskbits++;
954 0 : npackets = 1U<<nmaskbits;
955 0 : } while (1);
956 0 : }
957 0 : FD_LOG_DEBUG(("making bloom filter for %lu items with %lu packets and %lu keys %g error", nitems, npackets, nkeys, e));
958 :
959 : /* Generate random keys */
960 0 : ulong keys[FD_BLOOM_MAX_KEYS];
961 0 : for (ulong i = 0; i < nkeys; ++i)
962 0 : keys[i] = fd_rng_ulong(glob->rng);
963 : /* Set all the bits */
964 0 : ulong num_bits_set[FD_BLOOM_MAX_PACKETS];
965 0 : for (ulong i = 0; i < npackets; ++i)
966 0 : num_bits_set[i] = 0;
967 0 : #define CHUNKSIZE (FD_BLOOM_NUM_BITS/64U)
968 0 : ulong bits[CHUNKSIZE * FD_BLOOM_MAX_PACKETS];
969 0 : fd_memset(bits, 0, CHUNKSIZE*8U*npackets);
970 0 : ulong expire = FD_NANOSEC_TO_MILLI(glob->now) - FD_GOSSIP_VALUE_EXPIRE;
971 0 : for( fd_value_table_iter_t iter = fd_value_table_iter_init( glob->values );
972 0 : !fd_value_table_iter_done( glob->values, iter );
973 0 : iter = fd_value_table_iter_next( glob->values, iter ) ) {
974 0 : fd_value_elem_t * ele = fd_value_table_iter_ele( glob->values, iter );
975 0 : fd_hash_t * hash = &(ele->key);
976 : /* Purge expired values */
977 0 : if (ele->wallclock < expire) {
978 0 : fd_value_table_remove( glob->values, hash );
979 0 : continue;
980 0 : }
981 : /* Choose which filter packet based on the high bits in the hash */
982 0 : ulong index = (nmaskbits == 0 ? 0UL : ( hash->ul[0] >> (64U - nmaskbits) ));
983 0 : ulong * chunk = bits + (index*CHUNKSIZE);
984 0 : for (ulong i = 0; i < nkeys; ++i) {
985 0 : ulong pos = fd_gossip_bloom_pos(hash, keys[i], FD_BLOOM_NUM_BITS);
986 0 : ulong * j = chunk + (pos>>6U); /* divide by 64 */
987 0 : ulong bit = 1UL<<(pos & 63U);
988 0 : if (!((*j) & bit)) {
989 0 : *j |= bit;
990 0 : num_bits_set[index]++;
991 0 : }
992 0 : }
993 0 : }
994 :
995 : /* Assemble the packets */
996 0 : fd_gossip_msg_t gmsg;
997 0 : fd_gossip_msg_new_disc(&gmsg, fd_gossip_msg_enum_pull_req);
998 0 : fd_gossip_pull_req_t * req = &gmsg.inner.pull_req;
999 0 : fd_crds_filter_t * filter = &req->filter;
1000 0 : filter->mask_bits = nmaskbits;
1001 0 : filter->filter.keys_len = nkeys;
1002 0 : filter->filter.keys = keys;
1003 0 : fd_gossip_bitvec_u64_t * bitvec = &filter->filter.bits;
1004 0 : bitvec->len = FD_BLOOM_NUM_BITS;
1005 0 : bitvec->has_bits = 1;
1006 0 : bitvec->bits.vec_len = FD_BLOOM_NUM_BITS/64U;
1007 :
1008 : /* The "value" in the request is always my own contact info */
1009 0 : fd_crds_value_t * value = &req->value;
1010 0 : fd_crds_data_new_disc(&value->data, fd_crds_data_enum_contact_info_v1);
1011 0 : fd_gossip_contact_info_v1_t * ci = &value->data.inner.contact_info_v1;
1012 0 : fd_memcpy(ci, &glob->my_contact_info, sizeof(fd_gossip_contact_info_v1_t));
1013 0 : fd_gossip_sign_crds_value(glob, value);
1014 :
1015 0 : for (uint i = 0; i < npackets; ++i) {
1016 : /* Update the filter mask specific part */
1017 0 : filter->mask = (nmaskbits == 0 ? ~0UL : ((i << (64U - nmaskbits)) | (~0UL >> nmaskbits)));
1018 0 : filter->filter.num_bits_set = num_bits_set[i];
1019 0 : bitvec->bits.vec = bits + (i*CHUNKSIZE);
1020 0 : fd_gossip_send(glob, &ele->key, &gmsg);
1021 0 : }
1022 0 : }
1023 :
1024 : /* Handle a pong response */
1025 : static void
1026 0 : fd_gossip_handle_pong( fd_gossip_t * glob, const fd_gossip_peer_addr_t * from, fd_gossip_ping_t const * pong ) {
1027 0 : fd_active_elem_t * val = fd_active_table_query(glob->actives, from, NULL);
1028 0 : if (val == NULL) {
1029 0 : FD_LOG_DEBUG(("received pong too late"));
1030 0 : return;
1031 0 : }
1032 :
1033 0 : uchar pre_image[FD_PING_PRE_IMAGE_SZ];
1034 0 : fd_memcpy( pre_image, "SOLANA_PING_PONG", 16UL );
1035 0 : fd_memcpy( pre_image+16UL, val->pingtoken.uc, 32UL );
1036 :
1037 :
1038 0 : fd_hash_t pre_image_hash;
1039 0 : fd_sha256_hash( pre_image, FD_PING_PRE_IMAGE_SZ, pre_image_hash.uc );
1040 :
1041 : /* Confirm response hash token */
1042 0 : fd_sha256_t sha[1];
1043 0 : fd_sha256_init( sha );
1044 0 : fd_sha256_append( sha, "SOLANA_PING_PONG", 16UL );
1045 :
1046 0 : fd_sha256_append( sha, pre_image_hash.uc, 32UL );
1047 0 : fd_hash_t pongtoken;
1048 0 : fd_sha256_fini( sha, pongtoken.uc );
1049 0 : if (memcmp(pongtoken.uc, pong->token.uc, 32UL) != 0) {
1050 0 : FD_LOG_DEBUG(( "received pong with wrong token" ));
1051 0 : return;
1052 0 : }
1053 :
1054 : /* Verify the signature */
1055 0 : fd_sha512_t sha2[1];
1056 0 : if (fd_ed25519_verify( /* msg */ pong->token.uc,
1057 0 : /* sz */ 32UL,
1058 0 : /* sig */ pong->signature.uc,
1059 0 : /* public_key */ pong->from.uc,
1060 0 : sha2 )) {
1061 0 : FD_LOG_WARNING(("received pong with invalid signature"));
1062 0 : return;
1063 0 : }
1064 :
1065 0 : val->pongtime = glob->now;
1066 0 : fd_hash_copy(&val->id, &pong->from);
1067 :
1068 : /* Remember that this is a good peer */
1069 0 : fd_peer_elem_t * peerval = fd_peer_table_query(glob->peers, from, NULL);
1070 0 : if (peerval == NULL) {
1071 0 : if (fd_peer_table_is_full(glob->peers)) {
1072 0 : FD_LOG_DEBUG(("too many peers"));
1073 0 : return;
1074 0 : }
1075 0 : peerval = fd_peer_table_insert(glob->peers, from);
1076 0 : peerval->stake = 0;
1077 0 : }
1078 0 : peerval->wallclock = FD_NANOSEC_TO_MILLI(glob->now); /* In millisecs */
1079 0 : fd_hash_copy(&peerval->id, &pong->from);
1080 :
1081 0 : fd_weights_elem_t const * val2 = fd_weights_table_query_const( glob->weights, &val->id, NULL );
1082 0 : val->weight = ( val2 == NULL ? 1UL : val2->weight );
1083 :
1084 0 : }
1085 :
1086 : /* Initiate a ping/pong with a random active partner to confirm it is
1087 : still alive. */
1088 : static void
1089 0 : fd_gossip_random_ping( fd_gossip_t * glob, fd_pending_event_arg_t * arg ) {
1090 0 : (void)arg;
1091 :
1092 : /* Try again in 1 sec */
1093 0 : fd_pending_event_t * ev = fd_gossip_add_pending(glob, glob->now + (long)100e8);
1094 0 : if (ev) {
1095 0 : ev->fun = fd_gossip_random_ping;
1096 0 : }
1097 :
1098 0 : if (fd_pending_pool_free( glob->event_pool ) < 100U)
1099 0 : return;
1100 :
1101 0 : ulong cnt = fd_active_table_key_cnt(glob->actives);
1102 0 : if (cnt == 0 && glob->inactives_cnt == 0)
1103 0 : return;
1104 0 : fd_gossip_peer_addr_t * addr = NULL;
1105 0 : if (glob->inactives_cnt > 0 && cnt < FD_ACTIVE_KEY_MAX)
1106 : /* Try a new peer */
1107 0 : addr = glob->inactives + (--(glob->inactives_cnt));
1108 0 : else {
1109 : /* Choose a random active peer */
1110 0 : ulong i = fd_rng_ulong(glob->rng) % cnt;
1111 0 : ulong j = 0;
1112 0 : for( fd_active_table_iter_t iter = fd_active_table_iter_init( glob->actives );
1113 0 : !fd_active_table_iter_done( glob->actives, iter );
1114 0 : iter = fd_active_table_iter_next( glob->actives, iter ) ) {
1115 0 : if (i == j++) {
1116 0 : fd_active_elem_t * ele = fd_active_table_iter_ele( glob->actives, iter );
1117 0 : if (glob->now - ele->pingtime < (long)60e9) /* minute cooldown */
1118 0 : return;
1119 0 : ele->pingcount = 0;
1120 0 : ele->pongtime = 0;
1121 0 : addr = &(ele->key);
1122 0 : break;
1123 0 : }
1124 0 : }
1125 0 : }
1126 :
1127 0 : fd_pending_event_arg_t arg2;
1128 0 : fd_gossip_peer_addr_copy(&arg2.key, addr);
1129 0 : fd_gossip_make_ping(glob, &arg2);
1130 0 : }
1131 :
1132 : /* Process an incoming crds value */
1133 : static void
1134 0 : fd_gossip_recv_crds_value(fd_gossip_t * glob, const fd_gossip_peer_addr_t * from, fd_pubkey_t * pubkey, fd_crds_value_t* crd) {
1135 : /* Verify the signature */
1136 0 : ulong wallclock;
1137 0 : switch (crd->data.discriminant) {
1138 0 : case fd_crds_data_enum_contact_info_v1:
1139 0 : pubkey = &crd->data.inner.contact_info_v1.id;
1140 0 : wallclock = crd->data.inner.contact_info_v1.wallclock;
1141 0 : break;
1142 0 : case fd_crds_data_enum_vote:
1143 0 : pubkey = &crd->data.inner.vote.from;
1144 0 : wallclock = crd->data.inner.vote.wallclock;
1145 0 : break;
1146 0 : case fd_crds_data_enum_lowest_slot:
1147 0 : pubkey = &crd->data.inner.lowest_slot.from;
1148 0 : wallclock = crd->data.inner.lowest_slot.wallclock;
1149 0 : break;
1150 0 : case fd_crds_data_enum_snapshot_hashes:
1151 0 : pubkey = &crd->data.inner.snapshot_hashes.from;
1152 0 : wallclock = crd->data.inner.snapshot_hashes.wallclock;
1153 0 : break;
1154 0 : case fd_crds_data_enum_accounts_hashes:
1155 0 : pubkey = &crd->data.inner.accounts_hashes.from;
1156 0 : wallclock = crd->data.inner.accounts_hashes.wallclock;
1157 0 : break;
1158 0 : case fd_crds_data_enum_epoch_slots:
1159 0 : pubkey = &crd->data.inner.epoch_slots.from;
1160 0 : wallclock = crd->data.inner.epoch_slots.wallclock;
1161 0 : break;
1162 0 : case fd_crds_data_enum_version_v1:
1163 0 : pubkey = &crd->data.inner.version_v1.from;
1164 0 : wallclock = crd->data.inner.version_v1.wallclock;
1165 0 : break;
1166 0 : case fd_crds_data_enum_version_v2:
1167 0 : pubkey = &crd->data.inner.version_v2.from;
1168 0 : wallclock = crd->data.inner.version_v2.wallclock;
1169 0 : break;
1170 0 : case fd_crds_data_enum_node_instance:
1171 0 : pubkey = &crd->data.inner.node_instance.from;
1172 0 : wallclock = crd->data.inner.node_instance.wallclock;
1173 0 : break;
1174 0 : case fd_crds_data_enum_duplicate_shred:
1175 0 : pubkey = &crd->data.inner.duplicate_shred.from;
1176 0 : wallclock = crd->data.inner.duplicate_shred.wallclock;
1177 0 : break;
1178 0 : case fd_crds_data_enum_incremental_snapshot_hashes:
1179 0 : pubkey = &crd->data.inner.incremental_snapshot_hashes.from;
1180 0 : wallclock = crd->data.inner.incremental_snapshot_hashes.wallclock;
1181 0 : break;
1182 0 : case fd_crds_data_enum_contact_info_v2:
1183 0 : pubkey = &crd->data.inner.contact_info_v2.from;
1184 0 : wallclock = crd->data.inner.contact_info_v2.wallclock;
1185 0 : break;
1186 0 : case fd_crds_data_enum_restart_last_voted_fork_slots:
1187 0 : pubkey = &crd->data.inner.restart_last_voted_fork_slots.from;
1188 0 : wallclock = crd->data.inner.restart_last_voted_fork_slots.wallclock;
1189 0 : break;
1190 0 : case fd_crds_data_enum_restart_heaviest_fork:
1191 0 : pubkey = &crd->data.inner.restart_heaviest_fork.from;
1192 0 : wallclock = crd->data.inner.restart_heaviest_fork.wallclock;
1193 0 : break;
1194 0 : default:
1195 0 : wallclock = FD_NANOSEC_TO_MILLI(glob->now); /* In millisecs */
1196 0 : break;
1197 0 : }
1198 0 : if (memcmp(pubkey->uc, glob->public_key->uc, 32U) == 0)
1199 : /* Ignore my own messages */
1200 0 : return;
1201 0 : if( crd->data.discriminant>=FD_KNOWN_CRDS_ENUM_MAX ) {
1202 0 : return;
1203 0 : }
1204 :
1205 : /* Perform the value hash to get the value table key */
1206 0 : uchar buf[PACKET_DATA_SIZE];
1207 0 : fd_bincode_encode_ctx_t ctx;
1208 0 : ctx.data = buf;
1209 0 : ctx.dataend = buf + PACKET_DATA_SIZE;
1210 0 : if ( fd_crds_value_encode( crd, &ctx ) ) {
1211 0 : FD_LOG_ERR(("fd_crds_value_encode failed"));
1212 0 : return;
1213 0 : }
1214 0 : ulong datalen = (ulong)((uchar*)ctx.data - buf);
1215 0 : fd_sha256_t sha2[1];
1216 0 : fd_sha256_init( sha2 );
1217 0 : fd_sha256_append( sha2, buf, datalen );
1218 0 : fd_hash_t key;
1219 0 : fd_sha256_fini( sha2, key.uc );
1220 :
1221 0 : fd_msg_stats_elem_t * msg_stat = &glob->msg_stats[ crd->data.discriminant ];
1222 0 : msg_stat->total_cnt++;
1223 0 : msg_stat->bytes_rx_cnt += datalen;
1224 0 : fd_value_elem_t * msg = fd_value_table_query(glob->values, &key, NULL);
1225 0 : if (msg != NULL) {
1226 : /* Already have this value */
1227 0 : msg_stat->dups_cnt++;
1228 0 : glob->recv_dup_cnt++;
1229 0 : if (from != NULL) {
1230 : /* Record the dup in the receive statistics table */
1231 0 : fd_stats_elem_t * val = fd_stats_table_query(glob->stats, from, NULL);
1232 0 : if (val == NULL) {
1233 0 : if (!fd_stats_table_is_full(glob->stats)) {
1234 0 : val = fd_stats_table_insert(glob->stats, from);
1235 0 : val->dups_cnt = 0;
1236 0 : }
1237 0 : }
1238 0 : if (val != NULL) {
1239 0 : val->last = glob->now;
1240 0 : for (ulong i = 0; i < val->dups_cnt; ++i)
1241 0 : if (fd_hash_eq(&val->dups[i].origin, pubkey)) {
1242 0 : val->dups[i].cnt++;
1243 0 : goto found_origin;
1244 0 : }
1245 0 : if (val->dups_cnt < 8) {
1246 0 : ulong i = val->dups_cnt++;
1247 0 : fd_hash_copy(&val->dups[i].origin, pubkey);
1248 0 : val->dups[i].cnt = 1;
1249 0 : }
1250 0 : found_origin: ;
1251 0 : }
1252 0 : }
1253 0 : return;
1254 0 : }
1255 :
1256 : /* Verify signature against the encoded CRDS data */
1257 0 : uchar* data_buf = &buf[ sizeof(fd_signature_t) ];
1258 0 : fd_sha512_t sha[1];
1259 0 : if (fd_ed25519_verify( /* msg */ data_buf,
1260 0 : /* sz */ (ulong)((uchar*)ctx.data - data_buf),
1261 0 : /* sig */ crd->signature.uc,
1262 0 : /* public_key */ pubkey->uc,
1263 0 : sha )) {
1264 0 : FD_LOG_DEBUG(("received crds_value with invalid signature"));
1265 0 : return;
1266 0 : }
1267 :
1268 : /* Store the value for later pushing/duplicate detection */
1269 0 : glob->recv_nondup_cnt++;
1270 0 : if (fd_value_table_is_full(glob->values)) {
1271 0 : FD_LOG_DEBUG(("too many values"));
1272 0 : return;
1273 0 : }
1274 0 : msg = fd_value_table_insert(glob->values, &key);
1275 0 : msg->wallclock = wallclock;
1276 0 : fd_hash_copy(&msg->origin, pubkey);
1277 :
1278 : /* We store the serialized form of the full CRDS value */
1279 0 : fd_memcpy(msg->data, buf, datalen);
1280 0 : msg->datalen = datalen;
1281 :
1282 0 : if (glob->need_push_cnt < FD_NEED_PUSH_MAX) {
1283 : /* Remember that I need to push this value */
1284 0 : ulong i = ((glob->need_push_head + (glob->need_push_cnt++)) & (FD_NEED_PUSH_MAX-1U));
1285 0 : fd_hash_copy(glob->need_push + i, &key);
1286 0 : }
1287 :
1288 0 : if (crd->data.discriminant == fd_crds_data_enum_contact_info_v1) {
1289 0 : fd_gossip_contact_info_v1_t * info = &crd->data.inner.contact_info_v1;
1290 0 : if( fd_gossip_port_from_socketaddr(&info->gossip) != 0) {
1291 : /* Remember the peer */
1292 0 : fd_gossip_peer_addr_t pkey;
1293 0 : fd_memset(&pkey, 0, sizeof(pkey));
1294 0 : fd_gossip_from_soladdr(&pkey, &info->gossip);
1295 0 : fd_peer_elem_t * val = fd_peer_table_query(glob->peers, &pkey, NULL);
1296 0 : if (val == NULL) {
1297 0 : if (fd_peer_table_is_full(glob->peers)) {
1298 0 : FD_LOG_DEBUG(("too many peers"));
1299 0 : } else {
1300 0 : val = fd_peer_table_insert(glob->peers, &pkey);
1301 0 : if (glob->inactives_cnt < INACTIVES_MAX &&
1302 0 : fd_active_table_query(glob->actives, &pkey, NULL) == NULL) {
1303 : /* Queue this peer for later pinging */
1304 0 : fd_gossip_peer_addr_copy(glob->inactives + (glob->inactives_cnt++), &pkey);
1305 0 : }
1306 0 : }
1307 0 : }
1308 0 : if (val != NULL) {
1309 0 : val->wallclock = wallclock;
1310 0 : val->stake = 0;
1311 0 : fd_hash_copy(&val->id, &info->id);
1312 0 : }
1313 0 : }
1314 :
1315 0 : fd_gossip_peer_addr_t peer_addr = { .addr = crd->data.inner.contact_info_v1.gossip.inner.ip4.addr,
1316 0 : .port = fd_ushort_bswap( crd->data.inner.contact_info_v1.gossip.inner.ip4.port ) };
1317 0 : if (glob->my_contact_info.shred_version == 0U && fd_gossip_is_allowed_entrypoint( glob, &peer_addr )) {
1318 0 : FD_LOG_NOTICE(("using shred version %lu", (ulong)crd->data.inner.contact_info_v1.shred_version));
1319 0 : glob->my_contact_info.shred_version = crd->data.inner.contact_info_v1.shred_version;
1320 0 : }
1321 0 : }
1322 :
1323 0 : if (crd->data.discriminant == fd_crds_data_enum_contact_info_v2) {
1324 0 : fd_gossip_contact_info_v2_t * info = &crd->data.inner.contact_info_v2;
1325 0 : fd_gossip_socket_addr_t socket_addr;
1326 0 : if( fd_gossip_contact_info_v2_find_proto_ident( info, FD_GOSSIP_SOCKET_TAG_GOSSIP, &socket_addr ) ) {
1327 0 : if( fd_gossip_port_from_socketaddr( &socket_addr ) != 0) {
1328 : /* Remember the peer */
1329 0 : fd_gossip_peer_addr_t pkey;
1330 0 : fd_memset(&pkey, 0, sizeof(pkey));
1331 0 : fd_gossip_from_soladdr(&pkey, &socket_addr);
1332 0 : fd_peer_elem_t * val = fd_peer_table_query(glob->peers, &pkey, NULL);
1333 0 : if (val == NULL) {
1334 0 : if (fd_peer_table_is_full(glob->peers)) {
1335 0 : FD_LOG_DEBUG(("too many peers"));
1336 0 : } else {
1337 0 : val = fd_peer_table_insert(glob->peers, &pkey);
1338 0 : if (glob->inactives_cnt < INACTIVES_MAX &&
1339 0 : fd_active_table_query(glob->actives, &pkey, NULL) == NULL) {
1340 : /* Queue this peer for later pinging */
1341 0 : fd_gossip_peer_addr_copy(glob->inactives + (glob->inactives_cnt++), &pkey);
1342 0 : }
1343 0 : }
1344 0 : }
1345 0 : if (val != NULL) {
1346 0 : val->wallclock = wallclock;
1347 0 : val->stake = 0;
1348 0 : fd_hash_copy(&val->id, &info->from);
1349 0 : }
1350 0 : }
1351 :
1352 0 : fd_gossip_peer_addr_t peer_addr = { .addr = socket_addr.inner.ip4.addr,
1353 : /* FIXME: hardcode to ip4 inner? */
1354 0 : .port = fd_ushort_bswap( fd_gossip_port_from_socketaddr( &socket_addr ) ) };
1355 0 : if (glob->my_contact_info.shred_version == 0U && fd_gossip_is_allowed_entrypoint( glob, &peer_addr )) {
1356 0 : FD_LOG_NOTICE(("using shred version %lu", (ulong)crd->data.inner.contact_info_v2.shred_version));
1357 0 : glob->my_contact_info.shred_version = crd->data.inner.contact_info_v2.shred_version;
1358 0 : }
1359 0 : }
1360 0 : }
1361 :
1362 : /* Deliver the data upstream */
1363 0 : fd_gossip_unlock( glob );
1364 0 : (*glob->deliver_fun)(&crd->data, glob->deliver_arg);
1365 0 : fd_gossip_lock( glob );
1366 0 : }
1367 :
1368 : static int
1369 0 : verify_signable_data_with_prefix( fd_gossip_prune_msg_t * msg ) {
1370 0 : fd_gossip_prune_sign_data_with_prefix_t signdata[1] = {0};
1371 0 : signdata->prefix = (uchar *)&FD_GOSSIP_PRUNE_DATA_PREFIX;
1372 0 : signdata->prefix_len = 18UL;
1373 0 : signdata->data.pubkey = msg->data.pubkey;
1374 0 : signdata->data.prunes_len = msg->data.prunes_len;
1375 0 : signdata->data.prunes = msg->data.prunes;
1376 0 : signdata->data.destination = msg->data.destination;
1377 0 : signdata->data.wallclock = msg->data.wallclock;
1378 :
1379 0 : uchar buf[PACKET_DATA_SIZE];
1380 0 : fd_bincode_encode_ctx_t ctx;
1381 0 : ctx.data = buf;
1382 0 : ctx.dataend = buf + PACKET_DATA_SIZE;
1383 0 : if ( fd_gossip_prune_sign_data_with_prefix_encode( signdata, &ctx ) ) {
1384 0 : FD_LOG_WARNING(("fd_gossip_prune_sign_data_encode failed"));
1385 0 : return 1;
1386 0 : }
1387 :
1388 0 : fd_sha512_t sha[1];
1389 0 : return fd_ed25519_verify( /* msg */ buf,
1390 0 : /* sz */ (ulong)((uchar*)ctx.data - buf),
1391 0 : /* sig */ msg->data.signature.uc,
1392 0 : /* public_key */ msg->data.pubkey.uc,
1393 0 : sha );
1394 0 : }
1395 :
1396 : static int
1397 0 : verify_signable_data( fd_gossip_prune_msg_t * msg ) {
1398 0 : fd_gossip_prune_sign_data_t signdata;
1399 0 : signdata.pubkey = msg->data.pubkey;
1400 0 : signdata.prunes_len = msg->data.prunes_len;
1401 0 : signdata.prunes = msg->data.prunes;
1402 0 : signdata.destination = msg->data.destination;
1403 0 : signdata.wallclock = msg->data.wallclock;
1404 :
1405 0 : uchar buf[PACKET_DATA_SIZE];
1406 0 : fd_bincode_encode_ctx_t ctx;
1407 0 : ctx.data = buf;
1408 0 : ctx.dataend = buf + PACKET_DATA_SIZE;
1409 0 : if ( fd_gossip_prune_sign_data_encode( &signdata, &ctx ) ) {
1410 0 : FD_LOG_WARNING(("fd_gossip_prune_sign_data_encode failed"));
1411 0 : return 1;
1412 0 : }
1413 :
1414 0 : fd_sha512_t sha[1];
1415 0 : return fd_ed25519_verify( /* msg */ buf,
1416 0 : /* sz */ (ulong)((uchar*)ctx.data - buf),
1417 0 : /* sig */ msg->data.signature.uc,
1418 0 : /* public_key */ msg->data.pubkey.uc,
1419 0 : sha );
1420 0 : }
1421 :
1422 : /* Handle a prune request from somebody else */
1423 : static void
1424 0 : fd_gossip_handle_prune(fd_gossip_t * glob, const fd_gossip_peer_addr_t * from, fd_gossip_prune_msg_t * msg) {
1425 0 : (void)from;
1426 :
1427 : /* Confirm the message is for me */
1428 0 : if (memcmp(msg->data.destination.uc, glob->public_key->uc, 32U) != 0)
1429 0 : return;
1430 :
1431 : /* Try to verify the signed data either with the prefix and not the prefix */
1432 0 : if ( ! ( verify_signable_data( msg ) == FD_ED25519_SUCCESS ||
1433 0 : verify_signable_data_with_prefix( msg ) == FD_ED25519_SUCCESS ) ) {
1434 0 : FD_LOG_WARNING(( "received prune message with invalid signature" ));
1435 0 : return;
1436 0 : }
1437 :
1438 : /* Find the active push state which needs to be pruned */
1439 0 : fd_push_state_t* ps = NULL;
1440 0 : for (ulong i = 0; i < glob->push_states_cnt; ++i) {
1441 0 : fd_push_state_t* s = glob->push_states[i];
1442 0 : if (memcmp(msg->data.pubkey.uc, s->id.uc, 32U) == 0) {
1443 0 : ps = s;
1444 0 : break;
1445 0 : }
1446 0 : }
1447 0 : if (ps == NULL)
1448 0 : return;
1449 :
1450 : /* Set the bloom filter prune bits */
1451 0 : for (ulong i = 0; i < msg->data.prunes_len; ++i) {
1452 0 : fd_pubkey_t * p = msg->data.prunes + i;
1453 0 : for (ulong j = 0; j < FD_PRUNE_NUM_KEYS; ++j) {
1454 0 : ulong pos = fd_gossip_bloom_pos(p, ps->prune_keys[j], FD_PRUNE_NUM_BITS);
1455 0 : ulong * j = ps->prune_bits + (pos>>6U); /* divide by 64 */
1456 0 : ulong bit = 1UL<<(pos & 63U);
1457 0 : *j |= bit;
1458 0 : }
1459 0 : }
1460 0 : }
1461 :
1462 : static int
1463 : fd_gossip_push_value_nolock( fd_gossip_t * glob, fd_crds_data_t * data, fd_hash_t * key_opt );
1464 :
1465 : /* Push an updated version of my contact info into values */
1466 : static void
1467 0 : fd_gossip_push_updated_contact(fd_gossip_t * glob) {
1468 : /* See if we have a shred version yet */
1469 0 : if (glob->my_contact_info.shred_version == 0U)
1470 0 : return;
1471 : /* Update every 1 secs */
1472 0 : if (glob->now - glob->last_contact_time < (long)1e9)
1473 0 : return;
1474 :
1475 0 : if (glob->last_contact_time != 0) {
1476 : /* Remove the old contact value */
1477 0 : fd_value_elem_t * ele = fd_value_table_query(glob->values, &glob->last_contact_info_key, NULL);
1478 0 : if (ele != NULL) {
1479 0 : fd_value_table_remove( glob->values, &glob->last_contact_info_key );
1480 0 : }
1481 :
1482 : /* Remove the old version value */
1483 0 : ele = fd_value_table_query(glob->values, &glob->last_version_key, NULL);
1484 0 : if (ele != NULL) {
1485 0 : fd_value_table_remove( glob->values, &glob->last_version_key );
1486 0 : }
1487 :
1488 0 : ele = fd_value_table_query(glob->values, &glob->last_contact_info_v2_key, NULL);
1489 0 : if (ele != NULL) {
1490 0 : fd_value_table_remove( glob->values, &glob->last_contact_info_v2_key );
1491 0 : }
1492 :
1493 0 : ele = fd_value_table_query(glob->values, &glob->last_node_instance_key, NULL);
1494 0 : if (ele != NULL) {
1495 0 : fd_value_table_remove( glob->values, &glob->last_node_instance_key );
1496 0 : }
1497 0 : }
1498 :
1499 0 : glob->last_contact_time = glob->now;
1500 :
1501 0 : {
1502 0 : fd_crds_data_t crd;
1503 0 : fd_crds_data_new_disc(&crd, fd_crds_data_enum_contact_info_v1);
1504 0 : fd_gossip_contact_info_v1_t * ci = &crd.inner.contact_info_v1;
1505 0 : fd_memcpy(ci, &glob->my_contact_info, sizeof(fd_gossip_contact_info_v1_t));
1506 :
1507 0 : fd_gossip_push_value_nolock(glob, &crd, &glob->last_contact_info_key);
1508 0 : }
1509 :
1510 0 : {
1511 0 : fd_crds_data_t crd;
1512 0 : fd_crds_data_new_disc(&crd, fd_crds_data_enum_contact_info_v2);
1513 0 : fd_gossip_contact_info_v2_t * ci = &crd.inner.contact_info_v2;
1514 :
1515 0 : fd_gossip_ip_addr_t addrs[ 256 ] = {0};
1516 0 : fd_gossip_socket_entry_t sockets[ 256 ] = {0};
1517 : // uint extentions[ 1 ] = {0};
1518 0 : ci->addrs = addrs;
1519 0 : ci->sockets = sockets;
1520 :
1521 0 : ushort last_port = 0;
1522 0 : uchar cnt = 0;
1523 0 : for(;;) {
1524 0 : fd_gossip_socket_addr_t* min_socket = NULL;
1525 0 : fd_gossip_ip_addr_t min_addr[1] = {0};
1526 0 : ushort min_port = USHORT_MAX;
1527 0 : uchar min_key = 0;
1528 :
1529 0 : ushort gossip_port = fd_gossip_port_from_socketaddr( &glob->my_contact_info.gossip );
1530 0 : ushort serve_repair_port = fd_gossip_port_from_socketaddr( &glob->my_contact_info.serve_repair );
1531 0 : ushort tvu_port = fd_gossip_port_from_socketaddr( &glob->my_contact_info.tvu );
1532 0 : ushort tpu_port = fd_gossip_port_from_socketaddr( &glob->my_contact_info.tpu );
1533 0 : ushort tpu_quic_port = (ushort)(fd_gossip_port_from_socketaddr( &glob->my_contact_info.tpu ) + 6);
1534 0 : ushort tpu_vote_port = fd_gossip_port_from_socketaddr( &glob->my_contact_info.tpu_vote );
1535 0 : if( gossip_port > 0 && gossip_port > last_port && gossip_port < min_port ) {
1536 0 : min_key = FD_GOSSIP_SOCKET_TAG_GOSSIP;
1537 0 : min_socket = &glob->my_contact_info.gossip;
1538 0 : min_port = gossip_port;
1539 0 : }
1540 0 : if( serve_repair_port > 0 && serve_repair_port > last_port && serve_repair_port < min_port ) {
1541 0 : min_key = FD_GOSSIP_SOCKET_TAG_SERVE_REPAIR;
1542 0 : min_socket = &glob->my_contact_info.serve_repair;
1543 0 : min_port = serve_repair_port;
1544 0 : }
1545 0 : if( tvu_port > 0 && tvu_port > last_port && tvu_port < min_port ) {
1546 0 : min_key = FD_GOSSIP_SOCKET_TAG_TVU;
1547 0 : min_socket = &glob->my_contact_info.tvu;
1548 0 : min_port = tvu_port;
1549 0 : }
1550 0 : if( tpu_port > 0 && tpu_port > last_port && tpu_port < min_port ) {
1551 0 : min_key = FD_GOSSIP_SOCKET_TAG_TPU;
1552 0 : min_socket = &glob->my_contact_info.tpu;
1553 0 : min_port = tpu_port;
1554 0 : }
1555 0 : if( tpu_quic_port > 0 && tpu_quic_port > last_port && tpu_quic_port < min_port ) {
1556 0 : min_key = FD_GOSSIP_SOCKET_TAG_TPU_QUIC;
1557 0 : min_socket = &glob->my_contact_info.tpu;
1558 0 : min_port = tpu_quic_port;
1559 0 : }
1560 0 : if( tpu_vote_port > 0 && tpu_vote_port > last_port && tpu_vote_port < min_port ) {
1561 0 : min_key = FD_GOSSIP_SOCKET_TAG_TPU_VOTE;
1562 0 : min_socket = &glob->my_contact_info.tpu_vote;
1563 0 : min_port = tpu_vote_port;
1564 0 : }
1565 0 : if( min_port==USHORT_MAX ) {
1566 0 : break;
1567 0 : }
1568 :
1569 : /* ci->addrs[ cnt ] = min_socket->inner.{ip4,ip6}.addr */
1570 0 : fd_gossip_ipaddr_from_socketaddr( min_socket, &ci->addrs[ cnt ] );
1571 0 : ci->sockets[ cnt ].index = 0;
1572 0 : ci->sockets[ cnt ].offset = (ushort)( min_port - last_port );
1573 0 : ci->sockets[ cnt ].key = min_key;
1574 0 : cnt++;
1575 0 : last_port = min_port;
1576 :
1577 0 : if( min_key ==FD_GOSSIP_SOCKET_TAG_TPU) {
1578 0 : ci->addrs[ cnt ] = *min_addr;
1579 0 : ci->sockets[ cnt ].index = 0;
1580 0 : ci->sockets[ cnt ].offset = 0;
1581 0 : ci->sockets[ cnt ].key = FD_GOSSIP_SOCKET_TAG_TPU_VOTE;
1582 0 : cnt++;
1583 0 : last_port = min_port;
1584 0 : }
1585 0 : }
1586 :
1587 0 : ci->addrs_len = 1;
1588 0 : ci->sockets_len = cnt;
1589 : // ci->extensions_len = 0;
1590 : // ci->extensions = extentions;
1591 0 : ci->shred_version = glob->my_contact_info.shred_version;
1592 0 : ci->wallclock = FD_NANOSEC_TO_MILLI(glob->now);
1593 0 : ci->from = glob->my_contact_info.id;
1594 0 : ci->outset = 1UL;
1595 0 : ci->version.client = 2;
1596 0 : ci->version.commit = 42;
1597 0 : ci->version.feature_set = 42;
1598 0 : ci->version.major = 42;
1599 0 : ci->version.minor = 42;
1600 0 : ci->version.patch = 42;
1601 :
1602 0 : fd_gossip_push_value_nolock(glob, &crd, &glob->last_contact_info_v2_key);
1603 0 : }
1604 :
1605 0 : {
1606 0 : fd_crds_data_t crd;
1607 0 : fd_crds_data_new_disc(&crd, fd_crds_data_enum_node_instance);
1608 0 : fd_gossip_node_instance_t * node_instance = &crd.inner.node_instance;
1609 0 : node_instance->from = glob->my_contact_info.id;
1610 0 : node_instance->timestamp = (long)FD_NANOSEC_TO_MILLI(glob->now);
1611 0 : node_instance->wallclock = FD_NANOSEC_TO_MILLI(glob->now);
1612 0 : node_instance->token = fd_rng_ulong( glob->rng );
1613 :
1614 0 : fd_gossip_push_value_nolock(glob, &crd, &glob->last_node_instance_key);
1615 0 : }
1616 :
1617 0 : {
1618 0 : fd_crds_data_t crd;
1619 0 : fd_crds_data_new_disc(&crd, fd_crds_data_enum_version_v2);
1620 0 : fd_gossip_version_v2_t * version = &crd.inner.version_v2;
1621 0 : fd_memcpy(version, &glob->my_version, sizeof(fd_gossip_version_v2_t));
1622 :
1623 0 : fd_gossip_push_value_nolock(glob, &crd, &glob->last_version_key);
1624 0 : }
1625 0 : }
1626 :
1627 : /* Respond to a pull request */
1628 : static void
1629 0 : fd_gossip_handle_pull_req(fd_gossip_t * glob, const fd_gossip_peer_addr_t * from, fd_gossip_pull_req_t * msg) {
1630 0 : fd_active_elem_t * val = fd_active_table_query(glob->actives, from, NULL);
1631 0 : if (val == NULL || val->pongtime == 0) {
1632 : /* Ping new peers before responding to requests */
1633 0 : if (fd_pending_pool_free( glob->event_pool ) < 100U)
1634 0 : return;
1635 0 : fd_pending_event_arg_t arg2;
1636 0 : fd_gossip_peer_addr_copy(&arg2.key, from);
1637 0 : fd_gossip_make_ping(glob, &arg2);
1638 0 : return;
1639 0 : }
1640 :
1641 : /* Encode an empty pull response as a template */
1642 0 : fd_gossip_msg_t gmsg;
1643 0 : fd_gossip_msg_new_disc(&gmsg, fd_gossip_msg_enum_pull_resp);
1644 0 : fd_gossip_pull_resp_t * pull_resp = &gmsg.inner.pull_resp;
1645 0 : fd_hash_copy( &pull_resp->pubkey, glob->public_key );
1646 :
1647 0 : uchar buf[PACKET_DATA_SIZE];
1648 0 : fd_bincode_encode_ctx_t ctx;
1649 0 : ctx.data = buf;
1650 0 : ctx.dataend = buf + PACKET_DATA_SIZE;
1651 0 : if ( fd_gossip_msg_encode( &gmsg, &ctx ) ) {
1652 0 : FD_LOG_WARNING(("fd_gossip_msg_encode failed"));
1653 0 : return;
1654 0 : }
1655 : /* Reach into buffer to get the number of values */
1656 0 : uchar * newend = (uchar *)ctx.data;
1657 0 : ulong * crds_len = (ulong *)(newend - sizeof(ulong));
1658 :
1659 : /* Push an updated version of my contact info into values */
1660 0 : fd_gossip_push_updated_contact(glob);
1661 :
1662 : /* Apply the bloom filter to my table of values */
1663 0 : fd_crds_filter_t * filter = &msg->filter;
1664 0 : ulong nkeys = filter->filter.keys_len;
1665 0 : ulong * keys = filter->filter.keys;
1666 0 : fd_gossip_bitvec_u64_t * bitvec = &filter->filter.bits;
1667 0 : ulong * bitvec2 = bitvec->bits.vec;
1668 0 : ulong expire = FD_NANOSEC_TO_MILLI(glob->now) - FD_GOSSIP_PULL_TIMEOUT;
1669 0 : ulong hits = 0;
1670 0 : ulong misses = 0;
1671 0 : uint npackets = 0;
1672 0 : for( fd_value_table_iter_t iter = fd_value_table_iter_init( glob->values );
1673 0 : !fd_value_table_iter_done( glob->values, iter );
1674 0 : iter = fd_value_table_iter_next( glob->values, iter ) ) {
1675 0 : fd_value_elem_t * ele = fd_value_table_iter_ele( glob->values, iter );
1676 0 : fd_hash_t * hash = &(ele->key);
1677 0 : if (ele->wallclock < expire)
1678 0 : continue;
1679 : /* Execute the bloom filter */
1680 0 : if (filter->mask_bits != 0U) {
1681 0 : ulong m = (~0UL >> filter->mask_bits);
1682 0 : if ((hash->ul[0] | m) != filter->mask)
1683 0 : continue;
1684 0 : }
1685 0 : int miss = 0;
1686 0 : for (ulong i = 0; i < nkeys; ++i) {
1687 0 : ulong pos = fd_gossip_bloom_pos(hash, keys[i], bitvec->len);
1688 0 : ulong * j = bitvec2 + (pos>>6U); /* divide by 64 */
1689 0 : ulong bit = 1UL<<(pos & 63U);
1690 0 : if (!((*j) & bit)) {
1691 0 : miss = 1;
1692 0 : break;
1693 0 : }
1694 0 : }
1695 0 : if (!miss) {
1696 0 : hits++;
1697 0 : continue;
1698 0 : }
1699 0 : misses++;
1700 : /* Add the value in already encoded form */
1701 0 : if (newend + ele->datalen - buf > PACKET_DATA_SIZE) {
1702 : /* Packet is getting too large. Flush it */
1703 0 : ulong sz = (ulong)(newend - buf);
1704 0 : fd_gossip_send_raw(glob, from, buf, sz);
1705 0 : char tmp[100];
1706 0 : FD_LOG_DEBUG(("sent msg type %u to %s size=%lu", gmsg.discriminant, fd_gossip_addr_str(tmp, sizeof(tmp), from), sz));
1707 0 : ++npackets;
1708 0 : newend = (uchar *)ctx.data;
1709 0 : *crds_len = 0;
1710 0 : }
1711 0 : fd_memcpy(newend, ele->data, ele->datalen);
1712 0 : newend += ele->datalen;
1713 0 : (*crds_len)++;
1714 0 : }
1715 :
1716 : /* Flush final packet */
1717 0 : if (newend > (uchar *)ctx.data) {
1718 0 : ulong sz = (ulong)(newend - buf);
1719 0 : fd_gossip_send_raw(glob, from, buf, sz);
1720 0 : char tmp[100];
1721 0 : FD_LOG_DEBUG(("sent msg type %u to %s size=%lu", gmsg.discriminant, fd_gossip_addr_str(tmp, sizeof(tmp), from), sz));
1722 0 : ++npackets;
1723 0 : }
1724 :
1725 0 : if (misses)
1726 0 : FD_LOG_DEBUG(("responded to pull request with %lu values in %u packets (%lu filtered out)", misses, npackets, hits));
1727 0 : }
1728 :
1729 : /* Handle any gossip message */
1730 : static void
1731 0 : fd_gossip_recv(fd_gossip_t * glob, const fd_gossip_peer_addr_t * from, fd_gossip_msg_t * gmsg) {
1732 0 : switch (gmsg->discriminant) {
1733 0 : case fd_gossip_msg_enum_pull_req:
1734 0 : fd_gossip_handle_pull_req(glob, from, &gmsg->inner.pull_req);
1735 0 : break;
1736 0 : case fd_gossip_msg_enum_pull_resp: {
1737 0 : fd_gossip_pull_resp_t * pull_resp = &gmsg->inner.pull_resp;
1738 0 : for (ulong i = 0; i < pull_resp->crds_len; ++i)
1739 0 : fd_gossip_recv_crds_value(glob, NULL, &pull_resp->pubkey, pull_resp->crds + i);
1740 0 : break;
1741 0 : }
1742 0 : case fd_gossip_msg_enum_push_msg: {
1743 0 : fd_gossip_push_msg_t * push_msg = &gmsg->inner.push_msg;
1744 0 : for (ulong i = 0; i < push_msg->crds_len; ++i)
1745 0 : fd_gossip_recv_crds_value(glob, from, &push_msg->pubkey, push_msg->crds + i);
1746 0 : break;
1747 0 : }
1748 0 : case fd_gossip_msg_enum_prune_msg:
1749 0 : fd_gossip_handle_prune(glob, from, &gmsg->inner.prune_msg);
1750 0 : break;
1751 0 : case fd_gossip_msg_enum_ping:
1752 0 : fd_gossip_handle_ping(glob, from, &gmsg->inner.ping);
1753 0 : break;
1754 0 : case fd_gossip_msg_enum_pong:
1755 0 : fd_gossip_handle_pong(glob, from, &gmsg->inner.pong);
1756 0 : break;
1757 0 : }
1758 0 : }
1759 :
1760 : /* Initiate connection to a peer */
1761 : int
1762 0 : fd_gossip_add_active_peer( fd_gossip_t * glob, fd_gossip_peer_addr_t * addr ) {
1763 0 : fd_gossip_lock( glob );
1764 0 : fd_active_elem_t * val = fd_active_table_query(glob->actives, addr, NULL);
1765 0 : if (val == NULL) {
1766 0 : if (fd_active_table_is_full(glob->actives)) {
1767 0 : FD_LOG_WARNING(("too many actives"));
1768 0 : fd_gossip_unlock( glob );
1769 0 : return -1;
1770 0 : }
1771 0 : val = fd_active_table_insert(glob->actives, addr);
1772 0 : fd_active_new_value(val);
1773 0 : val->pingcount = 0; /* Incremented in fd_gossip_make_ping */
1774 0 : }
1775 0 : fd_gossip_unlock( glob );
1776 0 : return 0;
1777 0 : }
1778 :
1779 : /* Improve the set of active push states */
1780 : static void
1781 0 : fd_gossip_refresh_push_states( fd_gossip_t * glob, fd_pending_event_arg_t * arg ) {
1782 0 : (void)arg;
1783 :
1784 : /* Try again in 20 sec */
1785 0 : fd_pending_event_t * ev = fd_gossip_add_pending(glob, glob->now + (long)20e9);
1786 0 : if (ev) {
1787 0 : ev->fun = fd_gossip_refresh_push_states;
1788 0 : }
1789 :
1790 : /* Delete states which no longer have active peers */
1791 0 : for (ulong i = 0; i < glob->push_states_cnt; ++i) {
1792 0 : fd_push_state_t* s = glob->push_states[i];
1793 0 : if (fd_active_table_query(glob->actives, &s->addr, NULL) == NULL) {
1794 0 : fd_push_states_pool_ele_release(glob->push_states_pool, glob->push_states[i]);
1795 : /* Replace with the one at the end */
1796 0 : glob->push_states[i--] = glob->push_states[--(glob->push_states_cnt)];
1797 0 : }
1798 0 : }
1799 0 : if (glob->push_states_cnt == FD_PUSH_LIST_MAX) {
1800 : /* Delete the worst destination based prune count */
1801 0 : fd_push_state_t * worst_s = glob->push_states[0];
1802 0 : ulong worst_i = 0;
1803 0 : for (ulong i = 1; i < glob->push_states_cnt; ++i) {
1804 0 : fd_push_state_t* s = glob->push_states[i];
1805 0 : if (s->drop_cnt > worst_s->drop_cnt) {
1806 0 : worst_s = s;
1807 0 : worst_i = i;
1808 0 : }
1809 0 : }
1810 0 : fd_push_states_pool_ele_release(glob->push_states_pool, worst_s);
1811 : /* Replace with the one at the end */
1812 0 : glob->push_states[worst_i] = glob->push_states[--(glob->push_states_cnt)];
1813 0 : }
1814 :
1815 : /* Add random actives as new pushers */
1816 0 : int failcnt = 0;
1817 0 : while (glob->push_states_cnt < FD_PUSH_LIST_MAX && failcnt < 5) {
1818 0 : fd_active_elem_t * a = fd_gossip_random_active( glob );
1819 0 : if( a == NULL ) break;
1820 :
1821 0 : for (ulong i = 0; i < glob->push_states_cnt; ++i) {
1822 0 : fd_push_state_t* s = glob->push_states[i];
1823 0 : if (fd_gossip_peer_addr_eq(&s->addr, &a->key))
1824 0 : goto skipadd;
1825 0 : }
1826 0 : failcnt = 0;
1827 :
1828 : /* Build the pusher state */
1829 0 : fd_push_state_t * s = fd_push_states_pool_ele_acquire(glob->push_states_pool);
1830 0 : fd_memset(s, 0, sizeof(fd_push_state_t));
1831 0 : fd_gossip_peer_addr_copy(&s->addr, &a->key);
1832 0 : fd_hash_copy(&s->id, &a->id);
1833 0 : for (ulong j = 0; j < FD_PRUNE_NUM_KEYS; ++j)
1834 0 : s->prune_keys[j] = fd_rng_ulong(glob->rng);
1835 :
1836 : /* Encode an empty push msg template */
1837 0 : fd_gossip_msg_t gmsg[1] = {0};
1838 0 : fd_gossip_msg_new_disc(gmsg, fd_gossip_msg_enum_push_msg);
1839 0 : fd_gossip_push_msg_t * push_msg = &gmsg->inner.push_msg;
1840 0 : fd_hash_copy( &push_msg->pubkey, glob->public_key );
1841 0 : fd_bincode_encode_ctx_t ctx;
1842 0 : ctx.data = s->packet;
1843 0 : ctx.dataend = s->packet + PACKET_DATA_SIZE;
1844 0 : if ( fd_gossip_msg_encode( gmsg, &ctx ) ) {
1845 0 : FD_LOG_ERR(("fd_gossip_msg_encode failed"));
1846 0 : return;
1847 0 : }
1848 0 : s->packet_end_init = s->packet_end = (uchar *)ctx.data;
1849 :
1850 0 : glob->push_states[glob->push_states_cnt++] = s;
1851 0 : break;
1852 :
1853 0 : skipadd:
1854 0 : ++failcnt;
1855 0 : }
1856 0 : }
1857 :
1858 : /* Push the latest values */
1859 : static void
1860 0 : fd_gossip_push( fd_gossip_t * glob, fd_pending_event_arg_t * arg ) {
1861 0 : (void)arg;
1862 :
1863 : /* Try again in 100 msec */
1864 0 : fd_pending_event_t * ev = fd_gossip_add_pending(glob, glob->now + (long)1e8);
1865 0 : if (ev) {
1866 0 : ev->fun = fd_gossip_push;
1867 0 : }
1868 :
1869 : /* Push an updated version of my contact info into values */
1870 0 : fd_gossip_push_updated_contact(glob);
1871 :
1872 : /* Iterate across recent values */
1873 0 : ulong expire = FD_NANOSEC_TO_MILLI(glob->now) - FD_GOSSIP_PULL_TIMEOUT;
1874 0 : while (glob->need_push_cnt > 0) {
1875 0 : fd_hash_t * h = glob->need_push + ((glob->need_push_head++) & (FD_NEED_PUSH_MAX-1));
1876 0 : glob->need_push_cnt--;
1877 :
1878 0 : fd_value_elem_t * msg = fd_value_table_query(glob->values, h, NULL);
1879 0 : if (msg == NULL || msg->wallclock < expire)
1880 0 : continue;
1881 :
1882 : /* Iterate across push states */
1883 0 : ulong npush = 0;
1884 0 : for (ulong i = 0; i < glob->push_states_cnt && npush < FD_PUSH_VALUE_MAX; ++i) {
1885 0 : fd_push_state_t* s = glob->push_states[i];
1886 :
1887 : /* Apply the pruning bloom filter */
1888 0 : int pass = 0;
1889 0 : for (ulong j = 0; j < FD_PRUNE_NUM_KEYS; ++j) {
1890 0 : ulong pos = fd_gossip_bloom_pos(&msg->origin, s->prune_keys[j], FD_PRUNE_NUM_BITS);
1891 0 : ulong * j = s->prune_bits + (pos>>6U); /* divide by 64 */
1892 0 : ulong bit = 1UL<<(pos & 63U);
1893 0 : if (!(*j & bit)) {
1894 0 : pass = 1;
1895 0 : break;
1896 0 : }
1897 0 : }
1898 0 : if (!pass) {
1899 0 : s->drop_cnt++;
1900 0 : glob->not_push_cnt++;
1901 0 : continue;
1902 0 : }
1903 0 : glob->push_cnt++;
1904 0 : npush++;
1905 :
1906 0 : ulong * crds_len = (ulong *)(s->packet_end_init - sizeof(ulong));
1907 : /* Add the value in already encoded form */
1908 0 : if (s->packet_end + msg->datalen - s->packet > PACKET_DATA_SIZE) {
1909 : /* Packet is getting too large. Flush it */
1910 0 : ulong sz = (ulong)(s->packet_end - s->packet);
1911 0 : fd_gossip_send_raw(glob, &s->addr, s->packet, sz);
1912 0 : char tmp[100];
1913 0 : FD_LOG_DEBUG(("push to %s size=%lu", fd_gossip_addr_str(tmp, sizeof(tmp), &s->addr), sz));
1914 0 : s->packet_end = s->packet_end_init;
1915 0 : *crds_len = 0;
1916 0 : }
1917 0 : fd_memcpy(s->packet_end, msg->data, msg->datalen);
1918 0 : s->packet_end += msg->datalen;
1919 0 : (*crds_len)++;
1920 0 : }
1921 0 : }
1922 :
1923 : /* Flush partially full packets */
1924 0 : for (ulong i = 0; i < glob->push_states_cnt; ++i) {
1925 0 : fd_push_state_t* s = glob->push_states[i];
1926 0 : if (s->packet_end != s->packet_end_init) {
1927 0 : ulong * crds_len = (ulong *)(s->packet_end_init - sizeof(ulong));
1928 0 : ulong sz = (ulong)(s->packet_end - s->packet);
1929 0 : fd_gossip_send_raw(glob, &s->addr, s->packet, sz);
1930 0 : char tmp[100];
1931 0 : FD_LOG_DEBUG(("push to %s size=%lu", fd_gossip_addr_str(tmp, sizeof(tmp), &s->addr), sz));
1932 0 : s->packet_end = s->packet_end_init;
1933 0 : *crds_len = 0;
1934 0 : }
1935 0 : }
1936 0 : }
1937 :
1938 : /* Publish an outgoing value. The source id and wallclock are set by this function */
1939 : static int
1940 0 : fd_gossip_push_value_nolock( fd_gossip_t * glob, fd_crds_data_t * data, fd_hash_t * key_opt ) {
1941 : /* Wrap the data in a value stub. Sign it. */
1942 0 : fd_crds_value_t crd;
1943 0 : fd_memcpy(&crd.data, data, sizeof(fd_crds_data_t));
1944 0 : fd_gossip_sign_crds_value(glob, &crd);
1945 :
1946 : /* Perform the value hash to get the value table key */
1947 0 : uchar buf[PACKET_DATA_SIZE];
1948 0 : fd_bincode_encode_ctx_t ctx;
1949 0 : ctx.data = buf;
1950 0 : ctx.dataend = buf + PACKET_DATA_SIZE;
1951 0 : if ( fd_crds_value_encode( &crd, &ctx ) ) {
1952 0 : FD_LOG_ERR(("fd_crds_value_encode failed"));
1953 0 : return -1;
1954 0 : }
1955 0 : fd_sha256_t sha2[1];
1956 0 : fd_sha256_init( sha2 );
1957 0 : ulong datalen = (ulong)((uchar*)ctx.data - buf);
1958 0 : fd_sha256_append( sha2, buf, datalen );
1959 0 : fd_hash_t key;
1960 0 : fd_sha256_fini( sha2, key.uc );
1961 0 : if ( key_opt != NULL )
1962 0 : fd_hash_copy( key_opt, &key );
1963 :
1964 : /* Store the value for later pushing/duplicate detection */
1965 0 : fd_value_elem_t * msg = fd_value_table_query(glob->values, &key, NULL);
1966 0 : if (msg != NULL) {
1967 : /* Already have this value, which is strange! */
1968 0 : return -1;
1969 0 : }
1970 0 : if (fd_value_table_is_full(glob->values)) {
1971 0 : FD_LOG_DEBUG(("too many values"));
1972 0 : return -1;
1973 0 : }
1974 0 : msg = fd_value_table_insert(glob->values, &key);
1975 0 : msg->wallclock = FD_NANOSEC_TO_MILLI(glob->now); /* convert to ms */
1976 0 : fd_hash_copy(&msg->origin, glob->public_key);
1977 :
1978 : /* We store the serialized form for convenience */
1979 0 : fd_memcpy(msg->data, buf, datalen);
1980 0 : msg->datalen = datalen;
1981 :
1982 0 : if (glob->need_push_cnt < FD_NEED_PUSH_MAX) {
1983 : /* Remember that I need to push this value */
1984 0 : ulong i = ((glob->need_push_head + (glob->need_push_cnt++)) & (FD_NEED_PUSH_MAX-1U));
1985 0 : fd_hash_copy(glob->need_push + i, &key);
1986 0 : }
1987 0 : return 0;
1988 0 : }
1989 :
1990 : int
1991 0 : fd_gossip_push_value( fd_gossip_t * glob, fd_crds_data_t * data, fd_hash_t * key_opt ) {
1992 0 : fd_gossip_lock( glob );
1993 0 : int rc = fd_gossip_push_value_nolock( glob, data, key_opt );
1994 0 : fd_gossip_unlock( glob );
1995 0 : return rc;
1996 0 : }
1997 :
1998 : /* Periodically make prune messages */
1999 : static void
2000 0 : fd_gossip_make_prune( fd_gossip_t * glob, fd_pending_event_arg_t * arg ) {
2001 0 : (void)arg;
2002 :
2003 : /* Try again in 30 sec */
2004 0 : fd_pending_event_t * ev = fd_gossip_add_pending(glob, glob->now + (long)30e9);
2005 0 : if (ev) {
2006 0 : ev->fun = fd_gossip_make_prune;
2007 0 : }
2008 :
2009 0 : long expire = glob->now - (long)FD_GOSSIP_VALUE_EXPIRE*((long)1e6);
2010 0 : for( fd_stats_table_iter_t iter = fd_stats_table_iter_init( glob->stats );
2011 0 : !fd_stats_table_iter_done( glob->stats, iter );
2012 0 : iter = fd_stats_table_iter_next( glob->stats, iter ) ) {
2013 0 : fd_stats_elem_t * ele = fd_stats_table_iter_ele( glob->stats, iter );
2014 0 : if (ele->last < expire) {
2015 : /* Entry hasn't been updated for a long time */
2016 0 : fd_stats_table_remove( glob->stats, &ele->key );
2017 0 : continue;
2018 0 : }
2019 : /* Look for high duplicate counts */
2020 0 : fd_pubkey_t origins[8];
2021 0 : ulong origins_cnt = 0;
2022 0 : for (ulong i = 0; i < ele->dups_cnt; ++i) {
2023 0 : if (ele->dups[i].cnt >= 20U)
2024 0 : fd_hash_copy(&origins[origins_cnt++], &ele->dups[i].origin);
2025 0 : }
2026 0 : if (origins_cnt == 0U)
2027 0 : continue;
2028 : /* Get the peer id */
2029 0 : fd_peer_elem_t * peerval = fd_peer_table_query(glob->peers, &ele->key, NULL);
2030 : /* Always clean up to restart the dup counter */
2031 0 : fd_stats_table_remove( glob->stats, &ele->key );
2032 0 : if (peerval == NULL)
2033 0 : continue;
2034 :
2035 0 : char keystr[ FD_BASE58_ENCODED_32_SZ ];
2036 0 : fd_base58_encode_32( peerval->id.uc, NULL, keystr );
2037 0 : FD_LOG_DEBUG(("sending prune request for %lu origins to %s", origins_cnt, keystr));
2038 :
2039 : /* Make a prune request */
2040 0 : fd_gossip_msg_t gmsg;
2041 0 : fd_gossip_msg_new_disc(&gmsg, fd_gossip_msg_enum_prune_msg);
2042 0 : fd_gossip_prune_msg_t * prune_msg = &gmsg.inner.prune_msg;
2043 0 : fd_hash_copy(&prune_msg->data.pubkey, glob->public_key);
2044 0 : prune_msg->data.prunes_len = origins_cnt;
2045 0 : prune_msg->data.prunes = origins;;
2046 0 : fd_hash_copy(&prune_msg->data.destination, &peerval->id);
2047 0 : ulong wc = prune_msg->data.wallclock = FD_NANOSEC_TO_MILLI(glob->now);
2048 :
2049 0 : fd_gossip_prune_sign_data_t signdata;
2050 0 : fd_hash_copy(&signdata.pubkey, glob->public_key);
2051 0 : signdata.prunes_len = origins_cnt;
2052 0 : signdata.prunes = origins;;
2053 0 : fd_hash_copy(&signdata.destination, &peerval->id);
2054 0 : signdata.wallclock = wc;
2055 :
2056 0 : uchar buf[PACKET_DATA_SIZE];
2057 0 : fd_bincode_encode_ctx_t ctx;
2058 0 : ctx.data = buf;
2059 0 : ctx.dataend = buf + PACKET_DATA_SIZE;
2060 0 : if ( fd_gossip_prune_sign_data_encode( &signdata, &ctx ) ) {
2061 0 : FD_LOG_ERR(("fd_gossip_prune_sign_data_encode failed"));
2062 0 : return;
2063 0 : }
2064 :
2065 0 : (*glob->sign_fun)( glob->sign_arg, prune_msg->data.signature.uc, buf, (ulong)((uchar*)ctx.data - buf), FD_KEYGUARD_SIGN_TYPE_ED25519 );
2066 :
2067 0 : fd_gossip_send(glob, &peerval->key, &gmsg);
2068 0 : }
2069 0 : }
2070 :
2071 : /* Periodically log status. Removes old peers as a side event. */
2072 : static void
2073 0 : fd_gossip_log_stats( fd_gossip_t * glob, fd_pending_event_arg_t * arg ) {
2074 0 : (void)arg;
2075 :
2076 : /* Try again in 60 sec */
2077 0 : fd_pending_event_t * ev = fd_gossip_add_pending(glob, glob->now + (long)60e9);
2078 0 : if (ev) {
2079 0 : ev->fun = fd_gossip_log_stats;
2080 0 : }
2081 :
2082 0 : if( glob->recv_pkt_cnt == 0 )
2083 0 : FD_LOG_WARNING(("received no gossip packets!!"));
2084 0 : else
2085 0 : FD_LOG_INFO(("received %lu packets", glob->recv_pkt_cnt));
2086 0 : glob->recv_pkt_cnt = 0;
2087 0 : FD_LOG_INFO(("received %lu dup values and %lu new", glob->recv_dup_cnt, glob->recv_nondup_cnt));
2088 0 : glob->recv_dup_cnt = glob->recv_nondup_cnt = 0;
2089 0 : FD_LOG_INFO(("pushed %lu values and filtered %lu", glob->push_cnt, glob->not_push_cnt));
2090 0 : glob->push_cnt = glob->not_push_cnt = 0;
2091 :
2092 0 : for( ulong i = 0UL; i<FD_KNOWN_CRDS_ENUM_MAX; i++ ) {
2093 0 : FD_LOG_INFO(( "received values - type: %2lu, total: %12lu, dups: %12lu, bytes: %12lu", i, glob->msg_stats[i].total_cnt, glob->msg_stats[i].dups_cnt, glob->msg_stats[i].bytes_rx_cnt ));
2094 0 : }
2095 :
2096 0 : int need_inactive = (glob->inactives_cnt == 0);
2097 :
2098 0 : ulong wc = FD_NANOSEC_TO_MILLI(glob->now);
2099 0 : ulong expire = wc - 4U*FD_GOSSIP_VALUE_EXPIRE;
2100 0 : for( fd_peer_table_iter_t iter = fd_peer_table_iter_init( glob->peers );
2101 0 : !fd_peer_table_iter_done( glob->peers, iter );
2102 0 : iter = fd_peer_table_iter_next( glob->peers, iter ) ) {
2103 0 : fd_peer_elem_t * ele = fd_peer_table_iter_ele( glob->peers, iter );
2104 0 : if (ele->wallclock < expire) {
2105 : /* Peer hasn't been updated for a long time */
2106 0 : fd_peer_table_remove( glob->peers, &ele->key );
2107 0 : continue;
2108 0 : }
2109 0 : fd_active_elem_t * act = fd_active_table_query(glob->actives, &ele->key, NULL);
2110 0 : char buf[100];
2111 0 : char keystr[ FD_BASE58_ENCODED_32_SZ ];
2112 0 : fd_base58_encode_32( ele->id.uc, NULL, keystr );
2113 0 : FD_LOG_DEBUG(("peer at %s id %s age %.3f %s",
2114 0 : fd_gossip_addr_str(buf, sizeof(buf), &ele->key),
2115 0 : keystr,
2116 0 : ((double)(wc - ele->wallclock))*0.001,
2117 0 : ((act != NULL && act->pongtime != 0) ? "(active)" : "")));
2118 0 : if (need_inactive && act == NULL && glob->inactives_cnt < INACTIVES_MAX)
2119 0 : fd_gossip_peer_addr_copy(glob->inactives + (glob->inactives_cnt++), &ele->key);
2120 0 : }
2121 0 : }
2122 :
2123 : /* Set the current protocol time in nanosecs */
2124 : void
2125 0 : fd_gossip_settime( fd_gossip_t * glob, long ts ) {
2126 0 : glob->now = ts;
2127 0 : }
2128 :
2129 : /* Get the current protocol time in nanosecs */
2130 : long
2131 0 : fd_gossip_gettime( fd_gossip_t * glob ) {
2132 0 : return glob->now;
2133 0 : }
2134 :
2135 : /* Start timed events and other protocol behavior */
2136 : int
2137 0 : fd_gossip_start( fd_gossip_t * glob ) {
2138 0 : fd_gossip_lock( glob );
2139 : /* Start pulling and pinging on a timer */
2140 0 : fd_pending_event_t * ev = fd_gossip_add_pending(glob, glob->now + (long)1e9);
2141 0 : ev->fun = fd_gossip_random_pull;
2142 0 : ev = fd_gossip_add_pending(glob, glob->now + (long)5e9);
2143 0 : ev->fun = fd_gossip_random_ping;
2144 0 : ev = fd_gossip_add_pending(glob, glob->now + (long)60e9);
2145 0 : ev->fun = fd_gossip_log_stats;
2146 0 : ev = fd_gossip_add_pending(glob, glob->now + (long)20e9);
2147 0 : ev->fun = fd_gossip_refresh_push_states;
2148 0 : ev = fd_gossip_add_pending(glob, glob->now + (long)1e8);
2149 0 : ev->fun = fd_gossip_push;
2150 0 : ev = fd_gossip_add_pending(glob, glob->now + (long)30e9);
2151 0 : ev->fun = fd_gossip_make_prune;
2152 0 : fd_gossip_unlock( glob );
2153 :
2154 0 : return 0;
2155 0 : }
2156 :
2157 : /* Dispatch timed events and other protocol behavior. This should be
2158 : * called inside the main spin loop. */
2159 : int
2160 0 : fd_gossip_continue( fd_gossip_t * glob ) {
2161 0 : fd_gossip_lock( glob );
2162 0 : do {
2163 0 : fd_pending_event_t * ev = fd_pending_heap_ele_peek_min( glob->event_heap, glob->event_pool );
2164 0 : if (ev == NULL || ev->key > glob->now)
2165 0 : break;
2166 0 : fd_pending_event_t evcopy;
2167 0 : fd_memcpy(&evcopy, ev, sizeof(evcopy));
2168 0 : fd_pending_heap_ele_remove_min( glob->event_heap, glob->event_pool );
2169 0 : fd_pending_pool_ele_release( glob->event_pool, ev );
2170 0 : (*evcopy.fun)(glob, &evcopy.fun_arg);
2171 0 : } while (1);
2172 0 : fd_gossip_unlock( glob );
2173 0 : return 0;
2174 0 : }
2175 :
2176 : /* Pass a raw gossip packet into the protocol. msg_name is the unix socket address of the sender */
2177 : int
2178 0 : fd_gossip_recv_packet( fd_gossip_t * glob, uchar const * msg, ulong msglen, fd_gossip_peer_addr_t const * from ) {
2179 0 : fd_gossip_lock( glob );
2180 0 : FD_SCRATCH_SCOPE_BEGIN {
2181 0 : glob->recv_pkt_cnt++;
2182 : /* Deserialize the message */
2183 0 : fd_gossip_msg_t gmsg;
2184 0 : fd_bincode_decode_ctx_t ctx;
2185 0 : ctx.data = msg;
2186 0 : ctx.dataend = msg + msglen;
2187 0 : ctx.valloc = fd_scratch_virtual();
2188 0 : if (fd_gossip_msg_decode(&gmsg, &ctx)) {
2189 0 : FD_LOG_WARNING(("corrupt gossip message"));
2190 0 : fd_gossip_unlock( glob );
2191 0 : return -1;
2192 0 : }
2193 0 : if (ctx.data != ctx.dataend) {
2194 0 : FD_LOG_WARNING(("corrupt gossip message"));
2195 0 : fd_gossip_unlock( glob );
2196 0 : return -1;
2197 0 : }
2198 :
2199 0 : char tmp[100];
2200 :
2201 0 : FD_LOG_DEBUG(("recv msg type %u from %s", gmsg.discriminant, fd_gossip_addr_str(tmp, sizeof(tmp), from)));
2202 0 : fd_gossip_recv(glob, from, &gmsg);
2203 :
2204 0 : fd_gossip_unlock( glob );
2205 0 : } FD_SCRATCH_SCOPE_END;
2206 0 : return 0;
2207 0 : }
2208 :
2209 : ushort
2210 0 : fd_gossip_get_shred_version( fd_gossip_t const * glob ) {
2211 0 : return glob->my_contact_info.shred_version;
2212 0 : }
2213 :
2214 : void
2215 : fd_gossip_set_stake_weights( fd_gossip_t * gossip,
2216 : fd_stake_weight_t const * stake_weights,
2217 0 : ulong stake_weights_cnt ) {
2218 0 : if( stake_weights == NULL ) {
2219 0 : FD_LOG_ERR(( "stake weights NULL" ));
2220 0 : }
2221 :
2222 0 : if( stake_weights_cnt > MAX_STAKE_WEIGHTS ) {
2223 0 : FD_LOG_ERR(( "num stake weights (%lu) is larger than max allowed stake weights", stake_weights_cnt ));
2224 0 : }
2225 :
2226 0 : fd_gossip_lock( gossip );
2227 :
2228 : /* Clear out the table for new stake weights. */
2229 0 : for ( fd_weights_table_iter_t iter = fd_weights_table_iter_init( gossip->weights );
2230 0 : !fd_weights_table_iter_done( gossip->weights, iter);
2231 0 : iter = fd_weights_table_iter_next( gossip->weights, iter ) ) {
2232 0 : fd_weights_elem_t * e = fd_weights_table_iter_ele( gossip->weights, iter );
2233 0 : fd_weights_table_remove( gossip->weights, &e->key );
2234 0 : }
2235 :
2236 0 : for( ulong i = 0; i < stake_weights_cnt; ++i ) {
2237 0 : if( !stake_weights[i].stake ) continue;
2238 0 : fd_weights_elem_t * val = fd_weights_table_insert( gossip->weights, &stake_weights[i].key );
2239 : // Weight is log2(stake)^2
2240 0 : ulong w = (ulong)fd_ulong_find_msb( stake_weights[i].stake ) + 1;
2241 0 : val->weight = w*w;
2242 0 : }
2243 :
2244 0 : for( fd_active_table_iter_t iter = fd_active_table_iter_init( gossip->actives );
2245 0 : !fd_active_table_iter_done( gossip->actives, iter );
2246 0 : iter = fd_active_table_iter_next( gossip->actives, iter ) ) {
2247 0 : fd_active_elem_t * ele = fd_active_table_iter_ele( gossip->actives, iter );
2248 0 : fd_weights_elem_t const * val = fd_weights_table_query_const( gossip->weights, &ele->id, NULL );
2249 0 : ele->weight = ( val == NULL ? 1UL : val->weight );
2250 0 : }
2251 :
2252 0 : fd_gossip_unlock( gossip );
2253 0 : }
2254 :
2255 : void
2256 : fd_gossip_set_entrypoints( fd_gossip_t * gossip,
2257 : uint entrypoints[static 16],
2258 : ulong entrypoints_cnt,
2259 0 : ushort * ports ) {
2260 0 : gossip->entrypoints_cnt = entrypoints_cnt;
2261 0 : for( ulong i = 0UL; i<entrypoints_cnt; i++) {
2262 0 : fd_gossip_peer_addr_t addr;
2263 0 : addr.addr = entrypoints[i];
2264 0 : addr.port = fd_ushort_bswap( ports[i] );
2265 0 : FD_LOG_NOTICE(( "gossip initial peer - addr: " FD_IP4_ADDR_FMT ":%u",
2266 0 : FD_IP4_ADDR_FMT_ARGS( addr.addr ), fd_ushort_bswap( addr.port ) ));
2267 0 : fd_gossip_add_active_peer( gossip, &addr );
2268 0 : gossip->entrypoints[i] = addr;
2269 0 : }
2270 0 : }
2271 :
2272 : uint
2273 0 : fd_gossip_is_allowed_entrypoint( fd_gossip_t * gossip, fd_gossip_peer_addr_t * addr ) {
2274 0 : for( ulong i = 0UL; i<gossip->entrypoints_cnt; i++) {
2275 0 : if (fd_gossip_peer_addr_eq( addr, &gossip->entrypoints[i]) ) return 1;
2276 0 : }
2277 0 : return 0;
2278 0 : }
|