Line data Source code
1 : #include "fd_ping_tracker.h"
2 : #include "../../util/fd_hash32.h"
3 :
4 : #include "../../ballet/sha256/fd_sha256.h"
5 : #include "../../util/log/fd_log.h"
6 :
7 48 : #define FD_PING_TRACKER_STATE_UNPINGED (0)
8 66 : #define FD_PING_TRACKER_STATE_INVALID (1)
9 72 : #define FD_PING_TRACKER_STATE_VALID (2)
10 18 : #define FD_PING_TRACKER_STATE_VALID_REFRESHING (3)
11 :
12 : struct pubkey_private {
13 : uchar b[ 32UL ];
14 : };
15 :
16 : typedef struct pubkey_private pubkey_private_t;
17 :
18 : struct fd_ping_peer {
19 : fd_ip4_port_t address;
20 : pubkey_private_t identity_pubkey;
21 : uchar ping_token[ 32UL ];
22 : uchar expected_pong_hash[ 32UL ];
23 :
24 : uchar state;
25 :
26 : long next_ping_nanos;
27 : long valid_until_nanos;
28 : long last_rx_nanos;
29 :
30 : ulong pool_next;
31 :
32 : ulong lru_prev;
33 : ulong lru_next;
34 :
35 : ulong map_next;
36 : ulong map_prev;
37 :
38 : union {
39 : struct {
40 : ulong unpinged_next;
41 : ulong unpinged_prev;
42 : };
43 :
44 : struct {
45 : ulong waiting_next;
46 : ulong waiting_prev;
47 : };
48 :
49 : struct {
50 : ulong refreshing_next;
51 : ulong refreshing_prev;
52 : };
53 : };
54 : };
55 :
56 : typedef struct fd_ping_peer fd_ping_peer_t;
57 :
58 : #define POOL_NAME pool
59 1376316 : #define POOL_NEXT pool_next
60 42 : #define POOL_T fd_ping_peer_t
61 : #include "../../util/tmpl/fd_pool.c"
62 :
63 : #define DLIST_NAME lru_list
64 : #define DLIST_ELE_T fd_ping_peer_t
65 174 : #define DLIST_PREV lru_prev
66 174 : #define DLIST_NEXT lru_next
67 : #include "../../util/tmpl/fd_dlist.c"
68 :
69 : #define DLIST_NAME unpinged_list
70 : #define DLIST_ELE_T fd_ping_peer_t
71 48 : #define DLIST_PREV unpinged_prev
72 84 : #define DLIST_NEXT unpinged_next
73 : #include "../../util/tmpl/fd_dlist.c"
74 :
75 : #define DLIST_NAME waiting_list
76 : #define DLIST_ELE_T fd_ping_peer_t
77 30 : #define DLIST_PREV waiting_prev
78 36 : #define DLIST_NEXT waiting_next
79 : #include "../../util/tmpl/fd_dlist.c"
80 :
81 : #define DLIST_NAME refreshing_list
82 : #define DLIST_ELE_T fd_ping_peer_t
83 84 : #define DLIST_PREV refreshing_prev
84 96 : #define DLIST_NEXT refreshing_next
85 : #include "../../util/tmpl/fd_dlist.c"
86 :
87 : #define MAP_NAME peer_map
88 18 : #define MAP_ELE_T fd_ping_peer_t
89 : #define MAP_KEY_T pubkey_private_t
90 60 : #define MAP_KEY identity_pubkey
91 148755 : #define MAP_IDX_T ulong
92 75 : #define MAP_NEXT map_next
93 42 : #define MAP_PREV map_prev
94 74406 : #define MAP_KEY_HASH(k,s) fd_hash32( (k)->b, (s) )
95 90 : #define MAP_KEY_EQ(k0,k1) (!memcmp((k0)->b, (k1)->b, 32UL))
96 : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
97 : #include "../../util/tmpl/fd_map_chain.c"
98 :
99 : struct __attribute__((aligned(FD_PING_TRACKER_ALIGN))) fd_ping_tracker_private {
100 : fd_rng_t * rng;
101 : fd_sha256_t sha[1];
102 :
103 : ulong entrypoints_cnt;
104 : fd_ip4_port_t * entrypoints;
105 :
106 : fd_ping_tracker_metrics_t metrics[1];
107 :
108 : fd_ping_peer_t * pool;
109 : lru_list_t * lru;
110 :
111 : unpinged_list_t * unpinged;
112 : waiting_list_t * waiting;
113 : refreshing_list_t * refreshing;
114 :
115 : peer_map_t * peers;
116 :
117 : fd_ping_tracker_change_fn change_fn;
118 : void * change_fn_ctx;
119 :
120 : ulong magic; /* ==FD_PING_TRACKER_MAGIC */
121 : };
122 :
123 : FD_FN_CONST ulong
124 63 : fd_ping_tracker_align( void ) {
125 63 : return FD_PING_TRACKER_ALIGN;
126 63 : }
127 :
128 : FD_FN_CONST ulong
129 21 : fd_ping_tracker_footprint( ulong entrypoints_len ) {
130 21 : ulong l;
131 21 : l = FD_LAYOUT_INIT;
132 21 : l = FD_LAYOUT_APPEND( l, FD_PING_TRACKER_ALIGN, sizeof(fd_ping_tracker_t) );
133 21 : l = FD_LAYOUT_APPEND( l, alignof(fd_ip4_port_t), entrypoints_len*sizeof(fd_ip4_port_t) );
134 21 : l = FD_LAYOUT_APPEND( l, pool_align(), pool_footprint( FD_PING_TRACKER_MAX ) );
135 21 : l = FD_LAYOUT_APPEND( l, lru_list_align(), lru_list_footprint() );
136 21 : l = FD_LAYOUT_APPEND( l, unpinged_list_align(), unpinged_list_footprint() );
137 21 : l = FD_LAYOUT_APPEND( l, waiting_list_align(), waiting_list_footprint() );
138 21 : l = FD_LAYOUT_APPEND( l, refreshing_list_align(), refreshing_list_footprint() );
139 21 : l = FD_LAYOUT_APPEND( l, peer_map_align(), peer_map_footprint( 8192UL ) );
140 21 : return FD_LAYOUT_FINI( l, FD_PING_TRACKER_ALIGN );
141 21 : }
142 :
143 : void *
144 : fd_ping_tracker_new( void * shmem,
145 : fd_rng_t * rng,
146 : ulong entrypoints_len,
147 : fd_ip4_port_t const * entrypoints,
148 : fd_ping_tracker_change_fn change_fn,
149 21 : void * change_fn_ctx ) {
150 21 : if( FD_UNLIKELY( !shmem ) ) {
151 0 : FD_LOG_WARNING(( "NULL shmem" ));
152 0 : return NULL;
153 0 : }
154 :
155 21 : if( FD_UNLIKELY( !rng ) ) {
156 0 : FD_LOG_WARNING(( "NULL rng" ));
157 0 : return NULL;
158 0 : }
159 :
160 21 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_ping_tracker_align() ) ) ) {
161 0 : FD_LOG_WARNING(( "misaligned shmem" ));
162 0 : return NULL;
163 0 : }
164 :
165 21 : FD_SCRATCH_ALLOC_INIT( l, shmem );
166 21 : fd_ping_tracker_t * ping_tracker = FD_SCRATCH_ALLOC_APPEND( l, FD_PING_TRACKER_ALIGN, sizeof(fd_ping_tracker_t) );
167 21 : void * _entrypoints = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_ip4_port_t), entrypoints_len*sizeof(fd_ip4_port_t) );
168 21 : void * _pool = FD_SCRATCH_ALLOC_APPEND( l, pool_align(), pool_footprint( FD_PING_TRACKER_MAX ) );
169 21 : void * _lru = FD_SCRATCH_ALLOC_APPEND( l, lru_list_align(), lru_list_footprint() );
170 21 : void * _unpinged = FD_SCRATCH_ALLOC_APPEND( l, unpinged_list_align(), unpinged_list_footprint() );
171 21 : void * _waiting = FD_SCRATCH_ALLOC_APPEND( l, waiting_list_align(), waiting_list_footprint() );
172 21 : void * _refreshing = FD_SCRATCH_ALLOC_APPEND( l, refreshing_list_align(), refreshing_list_footprint() );
173 21 : void * _peers = FD_SCRATCH_ALLOC_APPEND( l, peer_map_align(), peer_map_footprint( 8192UL ) );
174 :
175 21 : ping_tracker->rng = rng;
176 21 : ping_tracker->pool = pool_join( pool_new( _pool, FD_PING_TRACKER_MAX ) );
177 21 : FD_TEST( ping_tracker->pool );
178 21 : ping_tracker->lru = lru_list_join( lru_list_new( _lru ) );
179 21 : FD_TEST( ping_tracker->lru );
180 21 : ping_tracker->unpinged = unpinged_list_join( unpinged_list_new( _unpinged ) );
181 21 : FD_TEST( ping_tracker->unpinged );
182 21 : ping_tracker->waiting = waiting_list_join( waiting_list_new( _waiting ) );
183 21 : FD_TEST( ping_tracker->waiting );
184 21 : ping_tracker->refreshing = refreshing_list_join( refreshing_list_new( _refreshing ) );
185 21 : FD_TEST( ping_tracker->refreshing );
186 21 : ping_tracker->peers = peer_map_join( peer_map_new( _peers, 8192UL, fd_rng_ulong( rng ) ) );
187 21 : FD_TEST( ping_tracker->peers );
188 :
189 21 : ping_tracker->entrypoints_cnt = entrypoints_len;
190 21 : ping_tracker->entrypoints = (fd_ip4_port_t *)_entrypoints;
191 21 : fd_memcpy( ping_tracker->entrypoints, entrypoints, entrypoints_len*sizeof(fd_ip4_port_t) );
192 :
193 21 : ping_tracker->change_fn = change_fn;
194 21 : ping_tracker->change_fn_ctx = change_fn_ctx;
195 :
196 21 : FD_TEST( fd_sha256_join( fd_sha256_new( ping_tracker->sha ) ) );
197 :
198 21 : fd_memset( ping_tracker->metrics, 0, sizeof(fd_ping_tracker_metrics_t) );
199 :
200 21 : FD_COMPILER_MFENCE();
201 21 : FD_VOLATILE( ping_tracker->magic ) = FD_PING_TRACKER_MAGIC;
202 21 : FD_COMPILER_MFENCE();
203 :
204 21 : return (void *)ping_tracker;
205 21 : }
206 :
207 : fd_ping_tracker_t *
208 21 : fd_ping_tracker_join( void * shpt ) {
209 21 : if( FD_UNLIKELY( !shpt ) ) {
210 0 : FD_LOG_WARNING(( "NULL shpt" ));
211 0 : return NULL;
212 0 : }
213 :
214 21 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shpt, fd_ping_tracker_align() ) ) ) {
215 0 : FD_LOG_WARNING(( "misaligned shpt" ));
216 0 : return NULL;
217 0 : }
218 :
219 21 : fd_ping_tracker_t * ping_tracker = (fd_ping_tracker_t *)shpt;
220 :
221 21 : if( FD_UNLIKELY( ping_tracker->magic!=FD_PING_TRACKER_MAGIC ) ) {
222 0 : FD_LOG_WARNING(( "bad magic" ));
223 0 : return NULL;
224 0 : }
225 :
226 21 : return ping_tracker;
227 21 : }
228 :
229 : static inline void
230 : hash_ping_token( uchar const * ping_token,
231 : uchar expected_pong_token[ static 32UL ],
232 45 : fd_sha256_t * sha ) {
233 45 : fd_sha256_init( sha );
234 45 : fd_sha256_append( sha, "SOLANA_PING_PONG", 16UL );
235 45 : fd_sha256_append( sha, ping_token, 32UL );
236 45 : fd_sha256_fini( sha, expected_pong_token );
237 45 : }
238 :
239 : static void
240 : remove_tracking( fd_ping_tracker_t * ping_tracker,
241 42 : fd_ping_peer_t * peer ) {
242 42 : if( FD_UNLIKELY( peer->state==FD_PING_TRACKER_STATE_UNPINGED ) ) unpinged_list_ele_remove( ping_tracker->unpinged, peer, ping_tracker->pool );
243 39 : else if( FD_LIKELY( peer->state==FD_PING_TRACKER_STATE_VALID ) ) waiting_list_ele_remove( ping_tracker->waiting, peer, ping_tracker->pool );
244 30 : else refreshing_list_ele_remove( ping_tracker->refreshing, peer, ping_tracker->pool );
245 42 : }
246 :
247 : static void
248 : generate_ping_token( fd_ping_peer_t * peer,
249 45 : fd_rng_t * rng ) {
250 45 : fd_memcpy( peer->ping_token, "SOLANA_PING_PONG", 16UL );
251 765 : for( ulong i=16UL; i<32UL; i++ ) peer->ping_token[ i ] = fd_rng_uchar( rng );
252 45 : }
253 :
254 : static inline void
255 : remove_peer( fd_ping_tracker_t * ping_tracker,
256 : fd_ping_peer_t * peer,
257 : long now,
258 18 : int change_type ) {
259 18 : peer_map_ele_remove_fast( ping_tracker->peers, peer, ping_tracker->pool );
260 18 : lru_list_ele_remove( ping_tracker->lru, peer, ping_tracker->pool );
261 18 : remove_tracking( ping_tracker, peer );
262 18 : if( FD_LIKELY( peer->state==FD_PING_TRACKER_STATE_VALID || peer->state==FD_PING_TRACKER_STATE_VALID_REFRESHING ) ) {
263 6 : ping_tracker->change_fn( ping_tracker->change_fn_ctx, peer->identity_pubkey.b, peer->address, now, change_type );
264 6 : }
265 18 : switch( peer->state ) {
266 3 : case FD_PING_TRACKER_STATE_UNPINGED: ping_tracker->metrics->unpinged_cnt--; break;
267 9 : case FD_PING_TRACKER_STATE_INVALID: ping_tracker->metrics->invalid_cnt--; break;
268 3 : case FD_PING_TRACKER_STATE_VALID: ping_tracker->metrics->valid_cnt--; break;
269 3 : case FD_PING_TRACKER_STATE_VALID_REFRESHING: ping_tracker->metrics->refreshing_cnt--; break;
270 0 : default: FD_LOG_ERR(( "Unknown state %d", peer->state )); return;
271 18 : }
272 18 : pool_ele_release( ping_tracker->pool, peer );
273 18 : }
274 :
275 : void
276 : fd_ping_tracker_track( fd_ping_tracker_t * ping_tracker,
277 : uchar const * peer_pubkey,
278 : ulong peer_stake,
279 : fd_ip4_port_t peer_address,
280 74259 : long now ) {
281 74259 : if( FD_UNLIKELY( !peer_address.addr ) ) return;
282 :
283 74259 : fd_ping_peer_t * peer = peer_map_ele_query( ping_tracker->peers, fd_type_pun_const( peer_pubkey ), NULL, ping_tracker->pool );
284 :
285 74259 : if( FD_UNLIKELY( !peer ) ) {
286 74244 : if( FD_LIKELY( peer_stake>=FD_GOSSIP_STAKED_THRESHOLD ) ) return;
287 :
288 42 : if( FD_UNLIKELY( !pool_free( ping_tracker->pool ) ) ) {
289 0 : peer = lru_list_ele_peek_head( ping_tracker->lru, ping_tracker->pool );
290 0 : remove_peer( ping_tracker, peer, now, FD_PING_TRACKER_CHANGE_TYPE_INACTIVE );
291 0 : ping_tracker->metrics->peers_evicted++;
292 0 : }
293 42 : peer = pool_ele_acquire( ping_tracker->pool );
294 :
295 42 : fd_memcpy( peer->identity_pubkey.b, peer_pubkey, 32UL );
296 42 : peer->address = peer_address;
297 42 : peer->valid_until_nanos = 0L;
298 42 : peer->next_ping_nanos = now;
299 42 : peer->state = FD_PING_TRACKER_STATE_UNPINGED;
300 42 : ping_tracker->metrics->unpinged_cnt++;
301 42 : ping_tracker->metrics->tracked_cnt++;
302 :
303 42 : generate_ping_token( peer, ping_tracker->rng );
304 42 : hash_ping_token( peer->ping_token, peer->expected_pong_hash, ping_tracker->sha );
305 :
306 42 : unpinged_list_ele_push_head( ping_tracker->unpinged, peer, ping_tracker->pool );
307 42 : peer_map_ele_insert( ping_tracker->peers, peer, ping_tracker->pool );
308 42 : lru_list_ele_push_tail( ping_tracker->lru, peer, ping_tracker->pool );
309 42 : } else {
310 15 : if( FD_LIKELY( peer_stake>=FD_GOSSIP_STAKED_THRESHOLD ) ) {
311 : /* Node went from unstaked (or low staked) to
312 : >=FD_GOSSIP_STAKED_THRESHOLD lamports. No longer need to ping
313 : it. */
314 0 : ping_tracker->metrics->stake_changed_cnt++;
315 0 : remove_peer( ping_tracker, peer, now, FD_PING_TRACKER_CHANGE_TYPE_INACTIVE_STAKED );
316 0 : return;
317 0 : }
318 :
319 15 : if( FD_UNLIKELY( peer_address.addr!=peer->address.addr || peer_address.port!=peer->address.port ) ) {
320 : /* Node changed address, update the address. Any existing pongs
321 : are no longer valid. */
322 3 : peer->address = peer_address;
323 3 : peer->valid_until_nanos = 0UL;
324 3 : remove_tracking( ping_tracker, peer );
325 3 : if( FD_LIKELY( peer->state==FD_PING_TRACKER_STATE_VALID || peer->state==FD_PING_TRACKER_STATE_VALID_REFRESHING ) ) {
326 3 : ping_tracker->change_fn( ping_tracker->change_fn_ctx, peer->identity_pubkey.b, peer->address, now, FD_PING_TRACKER_CHANGE_TYPE_INACTIVE );
327 3 : }
328 3 : ping_tracker->metrics->address_changed_cnt++;
329 3 : switch( peer->state ) {
330 0 : case FD_PING_TRACKER_STATE_UNPINGED: ping_tracker->metrics->unpinged_cnt--; break;
331 0 : case FD_PING_TRACKER_STATE_INVALID: ping_tracker->metrics->invalid_cnt--; break;
332 3 : case FD_PING_TRACKER_STATE_VALID: ping_tracker->metrics->valid_cnt--; break;
333 0 : case FD_PING_TRACKER_STATE_VALID_REFRESHING: ping_tracker->metrics->refreshing_cnt--; break;
334 0 : default: FD_LOG_ERR(( "Unknown state %d", peer->state )); return;
335 3 : }
336 3 : peer->next_ping_nanos = now;
337 3 : peer->state = FD_PING_TRACKER_STATE_UNPINGED;
338 3 : ping_tracker->metrics->unpinged_cnt++;
339 3 : generate_ping_token( peer, ping_tracker->rng );
340 3 : hash_ping_token( peer->ping_token, peer->expected_pong_hash, ping_tracker->sha );
341 :
342 3 : unpinged_list_ele_push_head( ping_tracker->unpinged, peer, ping_tracker->pool );
343 3 : }
344 15 : }
345 :
346 57 : peer->last_rx_nanos = now;
347 57 : lru_list_ele_remove( ping_tracker->lru, peer, ping_tracker->pool );
348 57 : lru_list_ele_push_tail( ping_tracker->lru, peer, ping_tracker->pool );
349 57 : }
350 :
351 : void
352 : fd_ping_tracker_register( fd_ping_tracker_t * ping_tracker,
353 : uchar const * peer_pubkey,
354 : ulong peer_stake,
355 : fd_ip4_port_t peer_address,
356 : uchar const * pong_token,
357 30 : long now ) {
358 30 : if( FD_UNLIKELY( peer_stake>=FD_GOSSIP_STAKED_THRESHOLD ) ) {
359 0 : ping_tracker->metrics->pong_result[ 0UL ]++;
360 0 : return;
361 0 : }
362 :
363 30 : fd_ping_peer_t * peer = peer_map_ele_query( ping_tracker->peers, fd_type_pun_const( peer_pubkey ), NULL, ping_tracker->pool );
364 30 : if( FD_UNLIKELY( !peer ) ) {
365 3 : ping_tracker->metrics->pong_result[ 2UL ]++;
366 3 : return;
367 3 : }
368 :
369 27 : if( FD_UNLIKELY( peer_address.addr!=peer->address.addr || peer_address.port!=peer->address.port ) ) {
370 3 : ping_tracker->metrics->pong_result[ 3UL ]++;
371 3 : return;
372 3 : }
373 24 : if( FD_UNLIKELY( memcmp( pong_token, peer->expected_pong_hash, 32UL ) ) ) {
374 3 : ping_tracker->metrics->pong_result[ 4UL ]++;
375 3 : return;
376 3 : }
377 :
378 21 : remove_tracking( ping_tracker, peer );
379 21 : peer->valid_until_nanos = now+20L*60L*1000L*1000L*1000L; /* 20 minutes of validity */
380 21 : peer->next_ping_nanos = now+18L*60L*1000L*1000L*1000L; /* 18 minutes til we start trying to refresh */
381 21 : if( FD_UNLIKELY( peer->state==FD_PING_TRACKER_STATE_INVALID || peer->state==FD_PING_TRACKER_STATE_UNPINGED ) ) {
382 18 : ping_tracker->change_fn( ping_tracker->change_fn_ctx, peer->identity_pubkey.b, peer->address, now, FD_PING_TRACKER_CHANGE_TYPE_ACTIVE );
383 18 : }
384 21 : switch( peer->state ) {
385 0 : case FD_PING_TRACKER_STATE_UNPINGED: ping_tracker->metrics->unpinged_cnt--; break;
386 18 : case FD_PING_TRACKER_STATE_INVALID: ping_tracker->metrics->invalid_cnt--; break;
387 3 : case FD_PING_TRACKER_STATE_VALID: ping_tracker->metrics->valid_cnt--; break;
388 0 : case FD_PING_TRACKER_STATE_VALID_REFRESHING: ping_tracker->metrics->refreshing_cnt--; break;
389 0 : default: FD_LOG_ERR(( "Unknown state %d", peer->state )); return;
390 21 : }
391 21 : peer->state = FD_PING_TRACKER_STATE_VALID;
392 21 : ping_tracker->metrics->valid_cnt++;
393 21 : waiting_list_ele_push_tail( ping_tracker->waiting, peer, ping_tracker->pool );
394 21 : ping_tracker->metrics->pong_result[ 5UL ]++;
395 21 : }
396 :
397 : int
398 : fd_ping_tracker_active( fd_ping_tracker_t * ping_tracker,
399 : uchar const * peer_pubkey,
400 42 : fd_ip4_port_t peer_address ) {
401 42 : if( FD_UNLIKELY( !peer_address.addr ) ) return 0;
402 36 : fd_ping_peer_t * peer = peer_map_ele_query( ping_tracker->peers, fd_type_pun_const( peer_pubkey ), NULL, ping_tracker->pool );
403 36 : if( FD_UNLIKELY( !peer ) ) return 0;
404 21 : return (peer->state==FD_PING_TRACKER_STATE_VALID || peer->state==FD_PING_TRACKER_STATE_VALID_REFRESHING) && peer->address.addr==peer_address.addr && peer->address.port==peer_address.port;
405 36 : }
406 :
407 : int
408 : fd_ping_tracker_pop_request( fd_ping_tracker_t * ping_tracker,
409 : long now,
410 : uchar const ** out_peer_pubkey,
411 : fd_ip4_port_t const ** out_peer_address,
412 75192 : uchar const ** out_token ) {
413 75192 : if( FD_UNLIKELY( !unpinged_list_is_empty( ping_tracker->unpinged, ping_tracker->pool ) ) ) {
414 36 : fd_ping_peer_t * unpinged = unpinged_list_ele_pop_head( ping_tracker->unpinged, ping_tracker->pool );
415 36 : FD_TEST( unpinged->state==FD_PING_TRACKER_STATE_UNPINGED );
416 36 : refreshing_list_ele_push_tail( ping_tracker->refreshing, unpinged, ping_tracker->pool );
417 36 : unpinged->state = FD_PING_TRACKER_STATE_INVALID;
418 36 : ping_tracker->metrics->unpinged_cnt--;
419 36 : ping_tracker->metrics->invalid_cnt++;
420 36 : unpinged->next_ping_nanos = now+20L*1000L*1000L*1000L;
421 36 : *out_peer_pubkey = unpinged->identity_pubkey.b;
422 36 : *out_peer_address = &unpinged->address;
423 36 : *out_token = unpinged->ping_token;
424 36 : return 1;
425 36 : }
426 :
427 75162 : for(;;) {
428 75162 : fd_ping_peer_t * peer_refreshing = NULL;
429 75162 : if( FD_UNLIKELY( !refreshing_list_is_empty( ping_tracker->refreshing, ping_tracker->pool ) ) ) peer_refreshing = refreshing_list_ele_peek_head( ping_tracker->refreshing, ping_tracker->pool );
430 75162 : fd_ping_peer_t * peer_waiting = NULL;
431 75162 : if( FD_UNLIKELY( !waiting_list_is_empty( ping_tracker->waiting, ping_tracker->pool ) ) ) peer_waiting = waiting_list_ele_peek_head( ping_tracker->waiting, ping_tracker->pool );
432 :
433 75162 : fd_ping_peer_t * next;
434 75162 : if( FD_UNLIKELY( !peer_refreshing && !peer_waiting ) ) return 0;
435 41946 : else if( FD_UNLIKELY( peer_refreshing && !peer_waiting ) ) next = peer_refreshing;
436 6 : else if( FD_UNLIKELY( !peer_refreshing && peer_waiting ) ) next = peer_waiting;
437 0 : else if( FD_UNLIKELY( peer_waiting->next_ping_nanos<peer_refreshing->next_ping_nanos ) ) next = peer_waiting;
438 0 : else next = peer_refreshing;
439 :
440 41946 : FD_TEST( next->state!=FD_PING_TRACKER_STATE_UNPINGED );
441 41946 : FD_TEST( next->next_ping_nanos );
442 41946 : if( FD_LIKELY( next->state!=FD_PING_TRACKER_STATE_INVALID ) ) FD_TEST( next->valid_until_nanos );
443 41937 : else FD_TEST( !next->valid_until_nanos );
444 :
445 41946 : if( FD_UNLIKELY( next->last_rx_nanos<now-60L*1000L*1000L*1000L ) ) {
446 : /* The peer is no longer sending us contact information, no need
447 : to ping it and instead remove it from the table. */
448 6 : remove_peer( ping_tracker, next, now, FD_PING_TRACKER_CHANGE_TYPE_INACTIVE );
449 6 : continue;
450 6 : }
451 :
452 : /* The next ping we want to send is still in the future, so do
453 : nothing for now. */
454 41940 : if( FD_LIKELY( next->next_ping_nanos>now ) ) return 0;
455 :
456 18 : if( FD_LIKELY( next==peer_refreshing ) ) refreshing_list_ele_pop_head( ping_tracker->refreshing, ping_tracker->pool );
457 6 : else if( FD_LIKELY( next==peer_waiting ) ) waiting_list_ele_pop_head( ping_tracker->waiting, ping_tracker->pool );
458 0 : else FD_LOG_CRIT(( "impossible" ));
459 :
460 : /* Push the element to the back of the refreshing list now, so it
461 : starts getting pinged every 20 seconds. */
462 18 : refreshing_list_ele_push_tail( ping_tracker->refreshing, next, ping_tracker->pool );
463 18 : if( FD_LIKELY( next->state==FD_PING_TRACKER_STATE_VALID ) ) {
464 6 : next->state = FD_PING_TRACKER_STATE_VALID_REFRESHING;
465 6 : ping_tracker->metrics->valid_cnt--;
466 6 : ping_tracker->metrics->refreshing_cnt++;
467 12 : } else if( FD_LIKELY( next->state==FD_PING_TRACKER_STATE_VALID_REFRESHING && next->valid_until_nanos<=now ) ) {
468 3 : ping_tracker->change_fn( ping_tracker->change_fn_ctx, next->identity_pubkey.b, next->address, now, FD_PING_TRACKER_CHANGE_TYPE_INACTIVE );
469 3 : switch( next->state ) {
470 0 : case FD_PING_TRACKER_STATE_UNPINGED: ping_tracker->metrics->unpinged_cnt--; break;
471 0 : case FD_PING_TRACKER_STATE_INVALID: ping_tracker->metrics->invalid_cnt--; break;
472 0 : case FD_PING_TRACKER_STATE_VALID: ping_tracker->metrics->valid_cnt--; break;
473 3 : case FD_PING_TRACKER_STATE_VALID_REFRESHING: ping_tracker->metrics->refreshing_cnt--; break;
474 0 : default: FD_LOG_ERR(( "Unknown state %d", next->state ));
475 3 : }
476 3 : next->state = FD_PING_TRACKER_STATE_INVALID;
477 3 : next->valid_until_nanos = 0L;
478 3 : ping_tracker->metrics->invalid_cnt++;
479 3 : }
480 18 : next->next_ping_nanos = now+20L*1000L*1000L*1000L;
481 18 : *out_peer_pubkey = next->identity_pubkey.b;
482 18 : *out_peer_address = &next->address;
483 18 : *out_token = next->ping_token;
484 18 : return 1;
485 18 : }
486 75156 : }
487 :
488 : void
489 : fd_ping_tracker_remove( fd_ping_tracker_t * ping_tracker,
490 : uchar const * peer_pubkey,
491 21 : long now ) {
492 21 : fd_ping_peer_t * peer = peer_map_ele_query( ping_tracker->peers, fd_type_pun_const( peer_pubkey ), NULL, ping_tracker->pool );
493 21 : if( FD_UNLIKELY( !peer ) ) return;
494 :
495 12 : remove_peer( ping_tracker, peer, now, FD_PING_TRACKER_CHANGE_TYPE_INACTIVE );
496 12 : }
497 :
498 : fd_ping_tracker_metrics_t const *
499 0 : fd_ping_tracker_metrics( fd_ping_tracker_t const * ping_tracker ) {
500 0 : return ping_tracker->metrics;
501 0 : }
|