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