Line data Source code
1 : #define _GNU_SOURCE 1
2 : #include "fd_repair.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/rng/fd_rng.h"
8 : #include "../../flamenco/fd_flamenco_base.h"
9 : #include <string.h>
10 : #include <stdio.h>
11 : #include <stdlib.h>
12 : #include <errno.h>
13 : #include <arpa/inet.h>
14 : #include <unistd.h>
15 : #include <sys/socket.h>
16 :
17 : void *
18 0 : fd_repair_new ( void * shmem, ulong seed ) {
19 0 : FD_SCRATCH_ALLOC_INIT(l, shmem);
20 0 : fd_repair_t * glob = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_repair_t), sizeof(fd_repair_t) );
21 0 : fd_memset(glob, 0, sizeof(fd_repair_t));
22 0 : void * shm = FD_SCRATCH_ALLOC_APPEND( l, fd_active_table_align(), fd_active_table_footprint(FD_ACTIVE_KEY_MAX) );
23 0 : glob->actives = fd_active_table_join(fd_active_table_new(shm, FD_ACTIVE_KEY_MAX, seed));
24 0 : glob->seed = seed;
25 0 : shm = FD_SCRATCH_ALLOC_APPEND( l, fd_inflight_table_align(), fd_inflight_table_footprint(FD_NEEDED_KEY_MAX) );
26 0 : glob->dupdetect = fd_inflight_table_join(fd_inflight_table_new(shm, FD_NEEDED_KEY_MAX, seed));
27 0 : shm = FD_SCRATCH_ALLOC_APPEND( l, fd_pinged_table_align(), fd_pinged_table_footprint(FD_REPAIR_PINGED_MAX) );
28 0 : glob->pinged = fd_pinged_table_join(fd_pinged_table_new(shm, FD_REPAIR_PINGED_MAX, seed));
29 0 : glob->stake_weights = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_stake_weight_t), FD_STAKE_WEIGHTS_MAX * sizeof(fd_stake_weight_t) );
30 0 : glob->stake_weights_temp = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_stake_weight_t), FD_STAKE_WEIGHTS_MAX * sizeof(fd_stake_weight_t) );
31 0 : glob->stake_weights_temp_cnt = 0;
32 0 : glob->stake_weights_cnt = 0;
33 0 : glob->last_decay = 0;
34 0 : glob->last_print = 0;
35 0 : glob->last_good_peer_cache_file_write = 0;
36 0 : glob->oldest_nonce = glob->current_nonce = glob->next_nonce = 0;
37 0 : fd_rng_new(glob->rng, (uint)seed, 0UL);
38 :
39 0 : glob->peer_cnt = 0;
40 0 : glob->peer_idx = 0;
41 0 : glob->actives_random_seed = 0;
42 :
43 0 : ulong scratch_top = FD_SCRATCH_ALLOC_FINI(l, 1UL);
44 0 : if ( scratch_top > (ulong)shmem + fd_repair_footprint() ) {
45 0 : FD_LOG_ERR(("Enough space not allocated for repair"));
46 0 : }
47 :
48 0 : return glob;
49 0 : }
50 :
51 : fd_repair_t *
52 0 : fd_repair_join ( void * shmap ) { return (fd_repair_t *)shmap; }
53 :
54 : void *
55 0 : fd_repair_leave ( fd_repair_t * join ) { return join; }
56 :
57 : void *
58 0 : fd_repair_delete ( void * shmap ) {
59 0 : fd_repair_t * glob = (fd_repair_t *)shmap;
60 0 : fd_active_table_delete( fd_active_table_leave( glob->actives ) );
61 0 : fd_inflight_table_delete( fd_inflight_table_leave( glob->dupdetect ) );
62 0 : fd_pinged_table_delete( fd_pinged_table_leave( glob->pinged ) );
63 0 : return glob;
64 0 : }
65 :
66 : /* Convert an address to a human readable string */
67 0 : const char * fd_repair_addr_str( char * dst, size_t dstlen, fd_repair_peer_addr_t const * src ) {
68 0 : char tmp[INET_ADDRSTRLEN];
69 0 : snprintf(dst, dstlen, "%s:%u", inet_ntop(AF_INET, &src->addr, tmp, INET_ADDRSTRLEN), (uint)ntohs(src->port));
70 0 : return dst;
71 0 : }
72 :
73 : /* Set the repair configuration */
74 : int
75 0 : fd_repair_set_config( fd_repair_t * glob, const fd_repair_config_t * config ) {
76 0 : char tmp[100];
77 0 : char keystr[ FD_BASE58_ENCODED_32_SZ ];
78 0 : fd_base58_encode_32( config->public_key->uc, NULL, keystr );
79 0 : FD_LOG_NOTICE(("configuring address %s key %s", fd_repair_addr_str(tmp, sizeof(tmp), &config->intake_addr), keystr));
80 :
81 0 : glob->public_key = config->public_key;
82 0 : glob->private_key = config->private_key;
83 0 : fd_repair_peer_addr_copy(&glob->intake_addr, &config->intake_addr);
84 0 : fd_repair_peer_addr_copy(&glob->service_addr, &config->service_addr);
85 0 : glob->good_peer_cache_file_fd = config->good_peer_cache_file_fd;
86 0 : return 0;
87 0 : }
88 :
89 : int
90 0 : fd_repair_update_addr( fd_repair_t * glob, const fd_repair_peer_addr_t * intake_addr, const fd_repair_peer_addr_t * service_addr ) {
91 0 : char tmp[100];
92 0 : FD_LOG_NOTICE(("updating address %s", fd_repair_addr_str(tmp, sizeof(tmp), intake_addr)));
93 :
94 0 : fd_repair_peer_addr_copy(&glob->intake_addr, intake_addr);
95 0 : fd_repair_peer_addr_copy(&glob->service_addr, service_addr);
96 0 : return 0;
97 0 : }
98 :
99 : /* Initiate connection to a peer */
100 : int
101 0 : fd_repair_add_active_peer( fd_repair_t * glob, fd_repair_peer_addr_t const * addr, fd_pubkey_t const * id ) {
102 0 : fd_active_elem_t * val = fd_active_table_query(glob->actives, id, NULL);
103 0 : if (val == NULL) {
104 0 : val = fd_active_table_insert(glob->actives, id);
105 0 : fd_repair_peer_addr_copy(&val->addr, addr);
106 0 : val->avg_reqs = 0;
107 0 : val->avg_reps = 0;
108 0 : val->avg_lat = 0;
109 0 : val->stake = 0UL;
110 :
111 0 : glob->peers[ glob->peer_cnt++ ] = (fd_peer_t){
112 0 : .key = *id,
113 0 : .ip4 = *addr
114 0 : };
115 0 : return 0;
116 0 : }
117 0 : return 1;
118 0 : }
119 :
120 : /* Set the current protocol time in nanosecs */
121 : void
122 0 : fd_repair_settime( fd_repair_t * glob, long ts ) {
123 0 : glob->now = ts;
124 0 : }
125 :
126 : /* Get the current protocol time in nanosecs */
127 : long
128 0 : fd_repair_gettime( fd_repair_t * glob ) {
129 0 : return glob->now;
130 0 : }
131 :
132 : static void
133 0 : fd_repair_decay_stats( fd_repair_t * glob ) {
134 0 : for( fd_active_table_iter_t iter = fd_active_table_iter_init( glob->actives );
135 0 : !fd_active_table_iter_done( glob->actives, iter );
136 0 : iter = fd_active_table_iter_next( glob->actives, iter ) ) {
137 0 : fd_active_elem_t * ele = fd_active_table_iter_ele( glob->actives, iter );
138 0 : #define DECAY(_v_) _v_ = _v_ - ((_v_)>>3U) /* Reduce by 12.5% */
139 0 : DECAY(ele->avg_reqs);
140 0 : DECAY(ele->avg_reps);
141 0 : DECAY(ele->avg_lat);
142 0 : #undef DECAY
143 0 : }
144 0 : }
145 :
146 : /**
147 : * read_line() reads characters one by one from 'fd' until:
148 : * - it sees a newline ('\n')
149 : * - it reaches 'max_len - 1' characters
150 : * - or EOF (read returns 0)
151 : * It stores the line in 'buf' and null-terminates it.
152 : *
153 : * Returns the number of characters read (not counting the null terminator),
154 : * or -1 on error.
155 : */
156 : static long
157 0 : read_line( int fd, char * buf ) {
158 0 : long i = 0;
159 :
160 0 : while (i < 255) {
161 0 : char c;
162 0 : long n = read(fd, &c, 1);
163 :
164 0 : if (n < 0) {
165 0 : if (errno == EINTR) continue;
166 0 : return -1;
167 0 : } else if (n == 0) {
168 0 : break;
169 0 : }
170 :
171 0 : buf[i++] = c;
172 :
173 0 : if (c == '\n') {
174 0 : break;
175 0 : }
176 0 : }
177 :
178 0 : buf[i] = '\0';
179 0 : return i;
180 0 : }
181 :
182 : static int
183 0 : fd_read_in_good_peer_cache_file( fd_repair_t * repair ) {
184 0 : if( repair->good_peer_cache_file_fd==-1 ) {
185 0 : FD_LOG_NOTICE(( "No repair good_peer_cache_file specified, not loading cached peers" ));
186 0 : return 0;
187 0 : }
188 :
189 0 : long seek = lseek( repair->good_peer_cache_file_fd, 0UL, SEEK_SET );
190 0 : if( FD_UNLIKELY( seek!=0L ) ) {
191 0 : FD_LOG_WARNING(( "Failed to seek to the beginning of the good peer cache file" ));
192 0 : return 1;
193 0 : }
194 :
195 0 : int loaded_peers = 0;
196 0 : char line[256];
197 0 : char *saveptr = NULL;
198 :
199 0 : long len;
200 0 : while ((len = read_line(repair->good_peer_cache_file_fd, line)) > 0) {
201 :
202 : /* Strip newline if present */
203 0 : size_t len = strlen( line );
204 0 : if( len>0 && line[len-1]=='\n' ) {
205 0 : line[len-1] = '\0';
206 0 : len--;
207 0 : }
208 :
209 : /* Skip empty or comment lines */
210 0 : if( !len || line[0]=='#' ) continue;
211 :
212 : /* Parse: base58EncodedPubkey/ipAddr/port */
213 0 : char * base58_str = strtok_r( line, "/", &saveptr );
214 0 : char * ip_str = strtok_r( NULL, "/", &saveptr );
215 0 : char * port_str = strtok_r( NULL, "/", &saveptr );
216 :
217 0 : if( FD_UNLIKELY( !base58_str || !ip_str || !port_str ) ) {
218 0 : FD_LOG_WARNING(( "Malformed line, skipping" ));
219 0 : continue;
220 0 : }
221 :
222 : /* Decode the base58 public key */
223 0 : fd_pubkey_t pubkey;
224 0 : if( !fd_base58_decode_32( base58_str, pubkey.uc ) ) {
225 0 : FD_LOG_WARNING(( "Failed to decode base58 public key '%s', skipping", base58_str ));
226 0 : continue;
227 0 : }
228 :
229 : /* Convert IP address */
230 0 : struct in_addr addr_parsed;
231 0 : if( inet_aton( ip_str, &addr_parsed )==0 ) {
232 0 : FD_LOG_WARNING(( "Invalid IPv4 address '%s', skipping", ip_str ));
233 0 : continue;
234 0 : }
235 :
236 : /* Convert the port */
237 0 : char * endptr = NULL;
238 0 : long port = strtol( port_str, &endptr, 10 );
239 0 : if( (port<=0L) || (port>65535L) || (endptr && *endptr!='\0') ) {
240 0 : FD_LOG_WARNING(( "Invalid port '%s', skipping", port_str ));
241 0 : continue;
242 0 : }
243 :
244 : /* Create the peer address struct (byte-swap the port to network order). */
245 : //fd_repair_peer_addr_t peer_addr;
246 : /* already in network byte order from inet_aton */
247 : //peer_addr.addr = ip_addr;
248 : /* Flip to big-endian for network order */
249 : //peer_addr.port = fd_ushort_bswap( (ushort)port );
250 :
251 : /* Add to active peers in the repair tile. */
252 : // fd_repair_add_active_peer( repair, &peer_addr, &pubkey );
253 :
254 0 : loaded_peers++;
255 0 : }
256 :
257 0 : FD_LOG_INFO(( "Loaded %d peers from good peer cache file", loaded_peers ));
258 0 : return 0;
259 0 : }
260 :
261 : /* Start timed events and other protocol behavior */
262 : int
263 0 : fd_repair_start( fd_repair_t * glob ) {
264 0 : glob->last_sends = glob->now;
265 0 : glob->last_decay = glob->now;
266 0 : glob->last_print = glob->now;
267 0 : return fd_read_in_good_peer_cache_file( glob );
268 0 : }
269 :
270 : static void fd_repair_print_all_stats( fd_repair_t * glob );
271 : static int fd_write_good_peer_cache_file( fd_repair_t * repair );
272 :
273 : /* Dispatch timed events and other protocol behavior. This should be
274 : * called inside the main spin loop. */
275 : int
276 0 : fd_repair_continue( fd_repair_t * glob ) {
277 0 : if ( glob->now - glob->last_print > (long)30e9 ) { /* 30 seconds */
278 0 : fd_repair_print_all_stats( glob );
279 0 : glob->last_print = glob->now;
280 0 : fd_repair_decay_stats( glob );
281 0 : glob->last_decay = glob->now;
282 0 : } else if ( glob->now - glob->last_decay > (long)15e9 ) { /* 15 seconds */
283 0 : fd_repair_decay_stats( glob );
284 0 : glob->last_decay = glob->now;
285 0 : } else if ( glob->now - glob->last_good_peer_cache_file_write > (long)60e9 ) { /* 1 minute */
286 0 : fd_write_good_peer_cache_file( glob );
287 0 : glob->last_good_peer_cache_file_write = glob->now;
288 0 : }
289 0 : return 0;
290 0 : }
291 :
292 : int
293 : fd_repair_construct_request_protocol( fd_repair_t * glob,
294 : fd_repair_protocol_t * protocol,
295 : enum fd_needed_elem_type type,
296 : ulong slot,
297 : uint shred_index,
298 : fd_pubkey_t const * recipient,
299 : uint nonce,
300 0 : long now ) {
301 0 : switch( type ) {
302 0 : case fd_needed_window_index: {
303 0 : glob->metrics.sent_pkt_types[FD_METRICS_ENUM_REPAIR_SENT_REQUEST_TYPES_V_NEEDED_WINDOW_IDX]++;
304 0 : fd_repair_protocol_new_disc(protocol, fd_repair_protocol_enum_window_index);
305 0 : fd_repair_window_index_t * wi = &protocol->inner.window_index;
306 0 : wi->header.sender = *glob->public_key;
307 0 : wi->header.recipient = *recipient;
308 0 : wi->header.timestamp = (ulong)now/1000000L;
309 0 : wi->header.nonce = nonce;
310 0 : wi->slot = slot;
311 0 : wi->shred_index = shred_index;
312 : //FD_LOG_INFO(( "repair request for %lu, %lu", wi->slot, wi->shred_index ));
313 0 : return 1;
314 0 : }
315 :
316 0 : case fd_needed_highest_window_index: {
317 0 : glob->metrics.sent_pkt_types[FD_METRICS_ENUM_REPAIR_SENT_REQUEST_TYPES_V_NEEDED_HIGHEST_WINDOW_IDX]++;
318 0 : fd_repair_protocol_new_disc( protocol, fd_repair_protocol_enum_highest_window_index );
319 0 : fd_repair_highest_window_index_t * wi = &protocol->inner.highest_window_index;
320 0 : wi->header.sender = *glob->public_key;
321 0 : wi->header.recipient = *recipient;
322 0 : wi->header.timestamp = (ulong)now/1000000L;
323 0 : wi->header.nonce = nonce;
324 0 : wi->slot = slot;
325 0 : wi->shred_index = shred_index;
326 : //FD_LOG_INFO(( "repair request for %lu, %lu", wi->slot, wi->shred_index ));
327 0 : return 1;
328 0 : }
329 :
330 0 : case fd_needed_orphan: {
331 0 : glob->metrics.sent_pkt_types[FD_METRICS_ENUM_REPAIR_SENT_REQUEST_TYPES_V_NEEDED_ORPHAN_IDX]++;
332 0 : fd_repair_protocol_new_disc( protocol, fd_repair_protocol_enum_orphan );
333 0 : fd_repair_orphan_t * wi = &protocol->inner.orphan;
334 0 : wi->header.sender = *glob->public_key;
335 0 : wi->header.recipient = *recipient;
336 0 : wi->header.timestamp = (ulong)now/1000000L;
337 0 : wi->header.nonce = nonce;
338 0 : wi->slot = slot;
339 : //FD_LOG_INFO(( "repair request for %lu", ele->dupkey.slot));
340 0 : return 1;
341 0 : }
342 0 : }
343 0 : return 0;
344 0 : }
345 :
346 : /* Returns 1 if its valid to send a request for the given shred. 0 if
347 : it is not, i.e., there is an inflight request for it that was sent
348 : within the last x ms. */
349 : static int
350 0 : fd_repair_create_inflight_request( fd_repair_t * glob, int type, ulong slot, uint shred_index, long now ) {
351 :
352 : /* If there are no active sticky peers from which to send requests to, refresh the sticky peers
353 : selection. It may be that stake weights were not available before, and now they are. */
354 :
355 0 : fd_inflight_key_t dupkey = { .type = (enum fd_needed_elem_type)type, .slot = slot, .shred_index = shred_index };
356 0 : fd_inflight_elem_t * dupelem = fd_inflight_table_query( glob->dupdetect, &dupkey, NULL );
357 :
358 0 : if( dupelem == NULL ) {
359 0 : dupelem = fd_inflight_table_insert( glob->dupdetect, &dupkey );
360 :
361 0 : if ( FD_UNLIKELY( dupelem == NULL ) ) {
362 0 : FD_LOG_ERR(( "Eviction unimplemented. Failed to insert duplicate detection element for slot %lu, shred_index %u", slot, shred_index ));
363 0 : return 0;
364 0 : }
365 :
366 0 : dupelem->last_send_time = 0L;
367 0 : }
368 :
369 0 : if( FD_LIKELY( dupelem->last_send_time+(long)40e6 < now ) ) { /* 40ms */
370 0 : dupelem->last_send_time = now;
371 0 : dupelem->req_cnt = FD_REPAIR_NUM_NEEDED_PEERS;
372 0 : return 1;
373 0 : }
374 0 : return 0;
375 0 : }
376 :
377 : int
378 : fd_repair_inflight_remove( fd_repair_t * glob,
379 : ulong slot,
380 0 : uint shred_index ) {
381 : /* If we have a shred, we can remove it from the inflight table */
382 : // FIXME: might be worth adding eviction logic here for orphan / highest window reqs
383 :
384 0 : fd_inflight_key_t dupkey = { .type = fd_needed_window_index, .slot = slot, .shred_index = shred_index };
385 0 : fd_inflight_elem_t * dupelem = fd_inflight_table_query( glob->dupdetect, &dupkey, NULL );
386 0 : if( dupelem ) {
387 : /* Remove the element from the inflight table */
388 0 : fd_inflight_table_remove( glob->dupdetect, &dupkey );
389 0 : }
390 0 : return 0;
391 0 : }
392 :
393 :
394 : static int
395 0 : fd_write_good_peer_cache_file( fd_repair_t * repair ) {
396 : // return 0;
397 :
398 0 : if ( repair->good_peer_cache_file_fd == -1 ) {
399 0 : return 0;
400 0 : }
401 :
402 0 : if ( repair->actives_sticky_cnt == 0 ) {
403 0 : return 0;
404 0 : }
405 :
406 : /* Truncate the file before we write it */
407 0 : int err = ftruncate( repair->good_peer_cache_file_fd, 0UL );
408 0 : if( FD_UNLIKELY( err==-1 ) ) {
409 0 : FD_LOG_WARNING(( "Failed to truncate the good peer cache file (%i-%s)", errno, fd_io_strerror( errno ) ));
410 0 : return 1;
411 0 : }
412 0 : long seek = lseek( repair->good_peer_cache_file_fd, 0UL, SEEK_SET );
413 0 : if( FD_UNLIKELY( seek!=0L ) ) {
414 0 : FD_LOG_WARNING(( "Failed to seek to the beginning of the good peer cache file" ));
415 0 : return 1;
416 0 : }
417 :
418 : /* Write the active sticky peers to file in the format:
419 : "base58EncodedPubkey/ipAddr/port"
420 :
421 : Where ipAddr is in dotted-decimal (e.g. "1.2.3.4")
422 : and port is decimal, in host order (e.g. "8001").
423 : */
424 0 : for( ulong i = 0UL; i < repair->actives_sticky_cnt; i++ ) {
425 0 : fd_pubkey_t * id = &repair->actives_sticky[ i ];
426 0 : fd_active_elem_t * peer = fd_active_table_query( repair->actives, id, NULL );
427 0 : if ( peer == NULL ) {
428 0 : continue;
429 0 : }
430 :
431 : /* Convert the public key to base58 */
432 0 : char base58_str[ FD_BASE58_ENCODED_32_SZ ];
433 0 : fd_base58_encode_32( peer->key.uc, NULL, base58_str );
434 :
435 : /* Convert the IP address to dotted-decimal string. The address
436 : in peer->addr.addr is already in network byte order. */
437 0 : struct in_addr addr_parsed;
438 0 : addr_parsed.s_addr = peer->addr.addr; /* net-order -> struct in_addr */
439 0 : char * ip_str = inet_ntoa( addr_parsed );
440 :
441 : /* Convert port from network byte order to host byte order. */
442 0 : ushort port = fd_ushort_bswap( peer->addr.port );
443 :
444 : /* Write out line: base58EncodedPubkey/ipAddr/port */
445 0 : dprintf( repair->good_peer_cache_file_fd, "%s/%s/%u\n", base58_str, ip_str, (uint)port );
446 0 : }
447 :
448 0 : return 0;
449 0 : }
450 :
451 : int
452 0 : fd_repair_need_window_index( fd_repair_t * glob, ulong slot, uint shred_index ) {
453 : // FD_LOG_NOTICE(( "[%s] need window %lu, shred_index %u", __func__, slot, shred_index ));
454 0 : return fd_repair_create_inflight_request( glob, fd_needed_window_index, slot, shred_index, glob->now );
455 0 : }
456 :
457 : int
458 0 : fd_repair_need_highest_window_index( fd_repair_t * glob, ulong slot, uint shred_index ) {
459 : //FD_LOG_DEBUG(( "[%s] need highest %lu", __func__, slot ));
460 0 : return fd_repair_create_inflight_request( glob, fd_needed_highest_window_index, slot, shred_index, glob->now );
461 0 : }
462 :
463 : int
464 0 : fd_repair_need_orphan( fd_repair_t * glob, ulong slot ) {
465 : // FD_LOG_NOTICE( ( "[repair] need orphan %lu", slot ) );
466 0 : return fd_repair_create_inflight_request( glob, fd_needed_orphan, slot, UINT_MAX, glob->now );
467 0 : }
468 :
469 : static void
470 0 : print_stats( fd_active_elem_t * val ) {
471 0 : fd_pubkey_t const * id = &val->key;
472 0 : if( FD_UNLIKELY( NULL == val ) ) return;
473 0 : if( val->avg_reqs == 0 )
474 0 : FD_LOG_INFO(( "repair peer %s: no requests sent, stake=%lu", FD_BASE58_ENC_32_ALLOCA( id ), val->stake / (ulong)1e9 ));
475 0 : else if( val->avg_reps == 0 )
476 0 : FD_LOG_INFO(( "repair peer %s: avg_requests=%lu, no responses received, stake=%lu", FD_BASE58_ENC_32_ALLOCA( id ), val->avg_reqs, val->stake / (ulong)1e9 ));
477 0 : else
478 0 : FD_LOG_INFO(( "repair peer %s: avg_requests=%lu, response_rate=%f, latency=%f, stake=%lu",
479 0 : FD_BASE58_ENC_32_ALLOCA( id ),
480 0 : val->avg_reqs,
481 0 : ((double)val->avg_reps)/((double)val->avg_reqs),
482 0 : 1.0e-9*((double)val->avg_lat)/((double)val->avg_reps),
483 0 : val->stake / (ulong)1e9 ));
484 0 : }
485 :
486 : static void
487 0 : fd_repair_print_all_stats( fd_repair_t * glob ) {
488 0 : for( fd_active_table_iter_t iter = fd_active_table_iter_init( glob->actives );
489 0 : !fd_active_table_iter_done( glob->actives, iter );
490 0 : iter = fd_active_table_iter_next( glob->actives, iter ) ) {
491 0 : fd_active_elem_t * val = fd_active_table_iter_ele( glob->actives, iter );
492 0 : print_stats( val );
493 0 : }
494 0 : FD_LOG_INFO( ( "peer count: %lu", fd_active_table_key_cnt( glob->actives ) ) );
495 0 : }
496 :
497 0 : void fd_repair_add_sticky( fd_repair_t * glob, fd_pubkey_t const * id ) {
498 0 : glob->actives_sticky[glob->actives_sticky_cnt++] = *id;
499 :
500 0 : }
501 :
502 : void
503 : fd_repair_set_stake_weights_init( fd_repair_t * repair,
504 : fd_stake_weight_t const * stake_weights,
505 0 : ulong stake_weights_cnt ) {
506 0 : if( stake_weights == NULL ) {
507 0 : FD_LOG_ERR(( "stake weights NULL" ));
508 0 : }
509 0 : if( stake_weights_cnt > FD_STAKE_WEIGHTS_MAX ) {
510 0 : FD_LOG_ERR(( "too many stake weights" ));
511 0 : }
512 :
513 0 : fd_memcpy( repair->stake_weights_temp, stake_weights, stake_weights_cnt * sizeof(fd_stake_weight_t) );
514 0 : repair->stake_weights_temp_cnt = stake_weights_cnt;
515 0 : }
516 :
517 : void
518 0 : fd_repair_set_stake_weights_fini( fd_repair_t * repair ) {
519 0 : fd_swap( repair->stake_weights, repair->stake_weights_temp );
520 0 : repair->stake_weights_cnt = repair->stake_weights_temp_cnt;
521 0 : }
522 :
523 :
524 : fd_repair_metrics_t *
525 0 : fd_repair_get_metrics( fd_repair_t * repair ) {
526 0 : return &repair->metrics;
527 0 : }
|