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 <string.h>
9 : #include <stdio.h>
10 : #include <stdlib.h>
11 : #include <errno.h>
12 : #include <arpa/inet.h>
13 : #include <unistd.h>
14 : #include <sys/socket.h>
15 :
16 : /* Max number of validators that can be actively queried */
17 0 : #define FD_ACTIVE_KEY_MAX (1<<12)
18 : /* Max number of pending shred requests */
19 0 : #define FD_NEEDED_KEY_MAX (1<<20)
20 : /* Max number of sticky repair peers */
21 0 : #define FD_REPAIR_STICKY_MAX 1024
22 : /* Max number of validator identities in stake weights */
23 0 : #define FD_STAKE_WEIGHTS_MAX (1<<14)
24 : /* Max number of validator clients that we ping */
25 0 : #define FD_REPAIR_PINGED_MAX (1<<14)
26 : /* Sha256 pre-image size for pings */
27 0 : #define FD_PING_PRE_IMAGE_SZ (48UL)
28 : /* Number of peers to send requests to. */
29 0 : #define FD_REPAIR_NUM_NEEDED_PEERS (4)
30 :
31 : /* Test if two hash values are equal */
32 0 : FD_FN_PURE static int fd_hash_eq( const fd_hash_t * key1, const fd_hash_t * key2 ) {
33 0 : for (ulong i = 0; i < 32U/sizeof(ulong); ++i)
34 0 : if (key1->ul[i] != key2->ul[i])
35 0 : return 0;
36 0 : return 1;
37 0 : }
38 :
39 : /* Hash a hash value */
40 0 : FD_FN_PURE static ulong fd_hash_hash( const fd_hash_t * key, ulong seed ) {
41 0 : return key->ul[0] ^ seed;
42 0 : }
43 :
44 : /* Copy a hash value */
45 0 : static void fd_hash_copy( fd_hash_t * keyd, const fd_hash_t * keys ) {
46 0 : for (ulong i = 0; i < 32U/sizeof(ulong); ++i)
47 0 : keyd->ul[i] = keys->ul[i];
48 0 : }
49 :
50 : /* Test if two addresses are equal */
51 0 : FD_FN_PURE int fd_repair_peer_addr_eq( const fd_repair_peer_addr_t * key1, const fd_repair_peer_addr_t * key2 ) {
52 0 : FD_STATIC_ASSERT(sizeof(fd_repair_peer_addr_t) == sizeof(ulong),"messed up size");
53 0 : return key1->l == key2->l;
54 0 : }
55 :
56 : /* Hash an address */
57 0 : FD_FN_PURE ulong fd_repair_peer_addr_hash( const fd_repair_peer_addr_t * key, ulong seed ) {
58 0 : FD_STATIC_ASSERT(sizeof(fd_repair_peer_addr_t) == sizeof(ulong),"messed up size");
59 0 : return (key->l + seed + 7242237688154252699UL)*9540121337UL;
60 0 : }
61 :
62 : /* Efficiently copy an address */
63 0 : void fd_repair_peer_addr_copy( fd_repair_peer_addr_t * keyd, const fd_repair_peer_addr_t * keys ) {
64 0 : FD_STATIC_ASSERT(sizeof(fd_repair_peer_addr_t) == sizeof(ulong),"messed up size");
65 0 : keyd->l = keys->l;
66 0 : }
67 :
68 : typedef uint fd_repair_nonce_t;
69 :
70 : /* Active table element. This table is all validators that we are
71 : asking for repairs. */
72 : struct fd_active_elem {
73 : fd_pubkey_t key; /* Public identifier and map key */
74 : ulong next; /* used internally by fd_map_giant */
75 :
76 : fd_repair_peer_addr_t addr;
77 : ulong avg_reqs; /* Moving average of the number of requests */
78 : ulong avg_reps; /* Moving average of the number of requests */
79 : long avg_lat; /* Moving average of response latency */
80 : uchar sticky;
81 : long first_request_time;
82 : ulong stake;
83 : };
84 : /* Active table */
85 : typedef struct fd_active_elem fd_active_elem_t;
86 : #define MAP_NAME fd_active_table
87 : #define MAP_KEY_T fd_pubkey_t
88 0 : #define MAP_KEY_EQ fd_hash_eq
89 0 : #define MAP_KEY_HASH fd_hash_hash
90 0 : #define MAP_KEY_COPY fd_hash_copy
91 0 : #define MAP_T fd_active_elem_t
92 : #include "../../util/tmpl/fd_map_giant.c"
93 :
94 : enum fd_needed_elem_type {
95 : fd_needed_window_index, fd_needed_highest_window_index, fd_needed_orphan
96 : };
97 :
98 : struct fd_dupdetect_key {
99 : enum fd_needed_elem_type type;
100 : ulong slot;
101 : uint shred_index;
102 : };
103 : typedef struct fd_dupdetect_key fd_dupdetect_key_t;
104 :
105 : struct fd_dupdetect_elem {
106 : fd_dupdetect_key_t key;
107 : long last_send_time;
108 : uint req_cnt;
109 : ulong next;
110 : };
111 : typedef struct fd_dupdetect_elem fd_dupdetect_elem_t;
112 :
113 : FD_FN_PURE
114 0 : int fd_dupdetect_eq( const fd_dupdetect_key_t * key1, const fd_dupdetect_key_t * key2 ) {
115 0 : return (key1->type == key2->type) &&
116 0 : (key1->slot == key2->slot) &&
117 0 : (key1->shred_index == key2->shred_index);
118 0 : }
119 :
120 : FD_FN_PURE
121 0 : ulong fd_dupdetect_hash( const fd_dupdetect_key_t * key, ulong seed ) {
122 0 : return (key->slot + seed)*9540121337UL + key->shred_index*131U;
123 0 : }
124 :
125 0 : void fd_dupdetect_copy( fd_dupdetect_key_t * keyd, const fd_dupdetect_key_t * keys ) {
126 0 : *keyd = *keys;
127 0 : }
128 :
129 : #define MAP_NAME fd_dupdetect_table
130 : #define MAP_KEY_T fd_dupdetect_key_t
131 0 : #define MAP_KEY_EQ fd_dupdetect_eq
132 0 : #define MAP_KEY_HASH fd_dupdetect_hash
133 0 : #define MAP_KEY_COPY fd_dupdetect_copy
134 0 : #define MAP_T fd_dupdetect_elem_t
135 : #include "../../util/tmpl/fd_map_giant.c"
136 :
137 0 : FD_FN_PURE int fd_repair_nonce_eq( const fd_repair_nonce_t * key1, const fd_repair_nonce_t * key2 ) {
138 0 : return *key1 == *key2;
139 0 : }
140 :
141 0 : FD_FN_PURE ulong fd_repair_nonce_hash( const fd_repair_nonce_t * key, ulong seed ) {
142 0 : return (*key + seed + 7242237688154252699UL)*9540121337UL;
143 0 : }
144 :
145 0 : void fd_repair_nonce_copy( fd_repair_nonce_t * keyd, const fd_repair_nonce_t * keys ) {
146 0 : *keyd = *keys;
147 0 : }
148 :
149 : struct fd_needed_elem {
150 : fd_repair_nonce_t key;
151 : ulong next;
152 : fd_pubkey_t id;
153 : fd_dupdetect_key_t dupkey;
154 : long when;
155 : };
156 : typedef struct fd_needed_elem fd_needed_elem_t;
157 : #define MAP_NAME fd_needed_table
158 : #define MAP_KEY_T fd_repair_nonce_t
159 0 : #define MAP_KEY_EQ fd_repair_nonce_eq
160 0 : #define MAP_KEY_HASH fd_repair_nonce_hash
161 0 : #define MAP_KEY_COPY fd_repair_nonce_copy
162 0 : #define MAP_T fd_needed_elem_t
163 : #include "../../util/tmpl/fd_map_giant.c"
164 :
165 : struct fd_pinged_elem {
166 : fd_repair_peer_addr_t key;
167 : ulong next;
168 : fd_pubkey_t id;
169 : fd_hash_t token;
170 : int good;
171 : };
172 : typedef struct fd_pinged_elem fd_pinged_elem_t;
173 : #define MAP_NAME fd_pinged_table
174 : #define MAP_KEY_T fd_repair_peer_addr_t
175 0 : #define MAP_KEY_EQ fd_repair_peer_addr_eq
176 0 : #define MAP_KEY_HASH fd_repair_peer_addr_hash
177 0 : #define MAP_KEY_COPY fd_repair_peer_addr_copy
178 0 : #define MAP_T fd_pinged_elem_t
179 : #include "../../util/tmpl/fd_map_giant.c"
180 :
181 : /* Global data for repair service */
182 : struct fd_repair {
183 : /* Concurrency lock */
184 : volatile ulong lock;
185 : /* Current time in nanosecs */
186 : long now;
187 : /* My public/private key */
188 : fd_pubkey_t * public_key;
189 : uchar * private_key;
190 : /* My repair addresses */
191 : fd_repair_peer_addr_t service_addr;
192 : fd_repair_peer_addr_t intake_addr;
193 : /* Function used to deliver repair messages to the application */
194 : fd_repair_shred_deliver_fun deliver_fun;
195 : /* Functions used to handle repair requests */
196 : fd_repair_serv_get_shred_fun serv_get_shred_fun;
197 : fd_repair_serv_get_parent_fun serv_get_parent_fun;
198 : /* Function used to send raw packets on the network */
199 : fd_repair_send_packet_fun clnt_send_fun; /* Client requests */
200 : fd_repair_send_packet_fun serv_send_fun; /* Service responses */
201 : /* Function used to send packets for signing to remote tile */
202 : fd_repair_sign_fun sign_fun;
203 : /* Argument to fd_repair_sign_fun */
204 : void * sign_arg;
205 : /* Function used to deliver repair failure on the network */
206 : fd_repair_shred_deliver_fail_fun deliver_fail_fun;
207 : void * fun_arg;
208 : /* Table of validators that we are actively pinging, keyed by repair address */
209 : fd_active_elem_t * actives;
210 : fd_pubkey_t actives_sticky[FD_REPAIR_STICKY_MAX]; /* cache of chosen repair peer samples */
211 : ulong actives_sticky_cnt;
212 : ulong actives_random_seed;
213 : /* Duplicate request detection table */
214 : fd_dupdetect_elem_t * dupdetect;
215 : /* Table of needed shreds */
216 : fd_needed_elem_t * needed;
217 : fd_repair_nonce_t oldest_nonce;
218 : fd_repair_nonce_t current_nonce;
219 : fd_repair_nonce_t next_nonce;
220 : /* Table of validator clients that we have pinged */
221 : fd_pinged_elem_t * pinged;
222 : /* Last batch of sends */
223 : long last_sends;
224 : /* Last statistics decay */
225 : long last_decay;
226 : /* Last statistics printout */
227 : long last_print;
228 : /* Last write to good peer cache file */
229 : long last_good_peer_cache_file_write;
230 : /* Random number generator */
231 : fd_rng_t rng[1];
232 : /* RNG seed */
233 : ulong seed;
234 : /* Stake weights */
235 : ulong stake_weights_cnt;
236 : fd_stake_weight_t * stake_weights;
237 : /* Path to the file where we write the cache of known good repair peers, to make cold booting faster */
238 : int good_peer_cache_file_fd;
239 : /* Metrics */
240 : fd_repair_metrics_t metrics;
241 : };
242 :
243 : FD_FN_CONST ulong
244 0 : fd_repair_align ( void ) { return 128UL; }
245 :
246 : FD_FN_CONST ulong
247 0 : fd_repair_footprint( void ) {
248 0 : ulong l = FD_LAYOUT_INIT;
249 0 : l = FD_LAYOUT_APPEND( l, alignof(fd_repair_t), sizeof(fd_repair_t) );
250 0 : l = FD_LAYOUT_APPEND( l, fd_active_table_align(), fd_active_table_footprint(FD_ACTIVE_KEY_MAX) );
251 0 : l = FD_LAYOUT_APPEND( l, fd_needed_table_align(), fd_needed_table_footprint(FD_NEEDED_KEY_MAX) );
252 0 : l = FD_LAYOUT_APPEND( l, fd_dupdetect_table_align(), fd_dupdetect_table_footprint(FD_NEEDED_KEY_MAX) );
253 0 : l = FD_LAYOUT_APPEND( l, fd_pinged_table_align(), fd_pinged_table_footprint(FD_REPAIR_PINGED_MAX) );
254 0 : l = FD_LAYOUT_APPEND( l, fd_stake_weight_align(), FD_STAKE_WEIGHTS_MAX * fd_stake_weight_footprint() );
255 0 : return FD_LAYOUT_FINI(l, fd_repair_align() );
256 0 : }
257 :
258 : void *
259 0 : fd_repair_new ( void * shmem, ulong seed ) {
260 0 : FD_SCRATCH_ALLOC_INIT(l, shmem);
261 0 : fd_repair_t * glob = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_repair_t), sizeof(fd_repair_t) );
262 0 : fd_memset(glob, 0, sizeof(fd_repair_t));
263 0 : void * shm = FD_SCRATCH_ALLOC_APPEND( l, fd_active_table_align(), fd_active_table_footprint(FD_ACTIVE_KEY_MAX) );
264 0 : glob->actives = fd_active_table_join(fd_active_table_new(shm, FD_ACTIVE_KEY_MAX, seed));
265 0 : glob->seed = seed;
266 0 : shm = FD_SCRATCH_ALLOC_APPEND( l, fd_needed_table_align(), fd_needed_table_footprint(FD_NEEDED_KEY_MAX) );
267 0 : glob->needed = fd_needed_table_join(fd_needed_table_new(shm, FD_NEEDED_KEY_MAX, seed));
268 0 : shm = FD_SCRATCH_ALLOC_APPEND( l, fd_dupdetect_table_align(), fd_dupdetect_table_footprint(FD_NEEDED_KEY_MAX) );
269 0 : glob->dupdetect = fd_dupdetect_table_join(fd_dupdetect_table_new(shm, FD_NEEDED_KEY_MAX, seed));
270 0 : shm = FD_SCRATCH_ALLOC_APPEND( l, fd_pinged_table_align(), fd_pinged_table_footprint(FD_REPAIR_PINGED_MAX) );
271 0 : glob->pinged = fd_pinged_table_join(fd_pinged_table_new(shm, FD_REPAIR_PINGED_MAX, seed));
272 0 : glob->stake_weights = FD_SCRATCH_ALLOC_APPEND( l, fd_stake_weight_align(), FD_STAKE_WEIGHTS_MAX * fd_stake_weight_footprint() );
273 0 : glob->stake_weights_cnt = 0;
274 0 : glob->last_sends = 0;
275 0 : glob->last_decay = 0;
276 0 : glob->last_print = 0;
277 0 : glob->last_good_peer_cache_file_write = 0;
278 0 : glob->oldest_nonce = glob->current_nonce = glob->next_nonce = 0;
279 0 : fd_rng_new(glob->rng, (uint)seed, 0UL);
280 :
281 0 : glob->actives_sticky_cnt = 0;
282 0 : glob->actives_random_seed = 0;
283 :
284 0 : ulong scratch_top = FD_SCRATCH_ALLOC_FINI(l, 1UL);
285 0 : if ( scratch_top > (ulong)shmem + fd_repair_footprint() ) {
286 0 : FD_LOG_ERR(("Enough space not allocated for repair"));
287 0 : }
288 :
289 0 : return glob;
290 0 : }
291 :
292 : fd_repair_t *
293 0 : fd_repair_join ( void * shmap ) { return (fd_repair_t *)shmap; }
294 :
295 : void *
296 0 : fd_repair_leave ( fd_repair_t * join ) { return join; }
297 :
298 : void *
299 0 : fd_repair_delete ( void * shmap ) {
300 0 : fd_repair_t * glob = (fd_repair_t *)shmap;
301 0 : fd_active_table_delete( fd_active_table_leave( glob->actives ) );
302 0 : fd_needed_table_delete( fd_needed_table_leave( glob->needed ) );
303 0 : fd_dupdetect_table_delete( fd_dupdetect_table_leave( glob->dupdetect ) );
304 0 : fd_pinged_table_delete( fd_pinged_table_leave( glob->pinged ) );
305 0 : return glob;
306 0 : }
307 :
308 : static void
309 0 : fd_repair_lock( fd_repair_t * repair ) {
310 0 : # if FD_HAS_THREADS
311 0 : for(;;) {
312 0 : if( FD_LIKELY( !FD_ATOMIC_CAS( &repair->lock, 0UL, 1UL) ) ) break;
313 0 : FD_SPIN_PAUSE();
314 0 : }
315 : # else
316 : repair->lock = 1;
317 : # endif
318 0 : FD_COMPILER_MFENCE();
319 0 : }
320 :
321 : static void
322 0 : fd_repair_unlock( fd_repair_t * repair ) {
323 0 : FD_COMPILER_MFENCE();
324 0 : FD_VOLATILE( repair->lock ) = 0UL;
325 0 : }
326 :
327 : /* Convert an address to a human readable string */
328 0 : const char * fd_repair_addr_str( char * dst, size_t dstlen, fd_repair_peer_addr_t const * src ) {
329 0 : char tmp[INET_ADDRSTRLEN];
330 0 : snprintf(dst, dstlen, "%s:%u", inet_ntop(AF_INET, &src->addr, tmp, INET_ADDRSTRLEN), (uint)ntohs(src->port));
331 0 : return dst;
332 0 : }
333 :
334 : /* Set the repair configuration */
335 : int
336 0 : fd_repair_set_config( fd_repair_t * glob, const fd_repair_config_t * config ) {
337 0 : char tmp[100];
338 0 : char keystr[ FD_BASE58_ENCODED_32_SZ ];
339 0 : fd_base58_encode_32( config->public_key->uc, NULL, keystr );
340 0 : FD_LOG_NOTICE(("configuring address %s key %s", fd_repair_addr_str(tmp, sizeof(tmp), &config->intake_addr), keystr));
341 :
342 0 : glob->public_key = config->public_key;
343 0 : glob->private_key = config->private_key;
344 0 : fd_repair_peer_addr_copy(&glob->intake_addr, &config->intake_addr);
345 0 : fd_repair_peer_addr_copy(&glob->service_addr, &config->service_addr);
346 0 : glob->deliver_fun = config->deliver_fun;
347 0 : glob->serv_get_shred_fun = config->serv_get_shred_fun;
348 0 : glob->serv_get_parent_fun = config->serv_get_parent_fun;
349 0 : glob->clnt_send_fun = config->clnt_send_fun;
350 0 : glob->serv_send_fun = config->serv_send_fun;
351 0 : glob->fun_arg = config->fun_arg;
352 0 : glob->sign_fun = config->sign_fun;
353 0 : glob->sign_arg = config->sign_arg;
354 0 : glob->deliver_fail_fun = config->deliver_fail_fun;
355 0 : glob->good_peer_cache_file_fd = config->good_peer_cache_file_fd;
356 0 : return 0;
357 0 : }
358 :
359 : int
360 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 ) {
361 0 : char tmp[100];
362 0 : FD_LOG_NOTICE(("updating address %s", fd_repair_addr_str(tmp, sizeof(tmp), intake_addr)));
363 :
364 0 : fd_repair_peer_addr_copy(&glob->intake_addr, intake_addr);
365 0 : fd_repair_peer_addr_copy(&glob->service_addr, service_addr);
366 0 : return 0;
367 0 : }
368 :
369 : /* Initiate connection to a peer */
370 : int
371 0 : fd_repair_add_active_peer( fd_repair_t * glob, fd_repair_peer_addr_t const * addr, fd_pubkey_t const * id ) {
372 0 : fd_repair_lock( glob );
373 0 : char tmp[100];
374 0 : char keystr[ FD_BASE58_ENCODED_32_SZ ];
375 0 : fd_base58_encode_32( id->uc, NULL, keystr );
376 0 : FD_LOG_DEBUG(("adding active peer address %s key %s", fd_repair_addr_str(tmp, sizeof(tmp), addr), keystr));
377 :
378 0 : fd_active_elem_t * val = fd_active_table_query(glob->actives, id, NULL);
379 0 : if (val == NULL) {
380 0 : if (fd_active_table_is_full(glob->actives)) {
381 0 : FD_LOG_WARNING(("too many active repair peers, discarding new peer"));
382 0 : fd_repair_unlock( glob );
383 0 : return -1;
384 0 : }
385 0 : val = fd_active_table_insert(glob->actives, id);
386 0 : fd_repair_peer_addr_copy(&val->addr, addr);
387 0 : val->avg_reqs = 0;
388 0 : val->avg_reps = 0;
389 0 : val->avg_lat = 0;
390 0 : val->sticky = 0;
391 0 : val->first_request_time = 0;
392 0 : val->stake = 0UL;
393 0 : FD_LOG_DEBUG(( "adding repair peer %s", FD_BASE58_ENC_32_ALLOCA( val->key.uc ) ));
394 0 : }
395 0 : fd_repair_unlock( glob );
396 0 : return 0;
397 0 : }
398 :
399 : /* Set the current protocol time in nanosecs */
400 : void
401 0 : fd_repair_settime( fd_repair_t * glob, long ts ) {
402 0 : glob->now = ts;
403 0 : }
404 :
405 : /* Get the current protocol time in nanosecs */
406 : long
407 0 : fd_repair_gettime( fd_repair_t * glob ) {
408 0 : return glob->now;
409 0 : }
410 :
411 : static void
412 : fd_repair_sign_and_send( fd_repair_t * glob,
413 : fd_repair_protocol_t * protocol,
414 0 : fd_gossip_peer_addr_t * addr ) {
415 :
416 0 : uchar _buf[1024];
417 0 : uchar * buf = _buf;
418 0 : ulong buflen = sizeof(_buf);
419 0 : fd_bincode_encode_ctx_t ctx = { .data = buf, .dataend = buf + buflen };
420 0 : if( FD_UNLIKELY( fd_repair_protocol_encode( protocol, &ctx ) != FD_BINCODE_SUCCESS ) ) {
421 0 : FD_LOG_CRIT(( "Failed to encode repair message (type %#x)", protocol->discriminant ));
422 0 : }
423 :
424 0 : buflen = (ulong)ctx.data - (ulong)buf;
425 0 : if( FD_UNLIKELY( buflen<68 ) ) {
426 0 : FD_LOG_CRIT(( "Attempted to sign unsigned repair message type (type %#x)", protocol->discriminant ));
427 0 : }
428 :
429 : /* At this point buffer contains
430 :
431 : [ discriminant ] [ signature ] [ payload ]
432 : ^ ^ ^
433 : 0 4 68 */
434 :
435 : /* https://github.com/solana-labs/solana/blob/master/core/src/repair/serve_repair.rs#L874 */
436 :
437 0 : fd_memcpy( buf+64, buf, 4 );
438 0 : buf += 64UL;
439 0 : buflen -= 64UL;
440 :
441 : /* Now it contains
442 :
443 : [ discriminant ] [ payload ]
444 : ^ ^
445 : 0 4 */
446 :
447 0 : fd_signature_t sig;
448 0 : (*glob->sign_fun)( glob->sign_arg, sig.uc, buf, buflen, FD_KEYGUARD_SIGN_TYPE_ED25519 );
449 :
450 : /* Reintroduce the signature */
451 :
452 0 : buf -= 64UL;
453 0 : buflen += 64UL;
454 0 : fd_memcpy( buf + 4U, &sig, 64U );
455 :
456 0 : uint src_ip4_addr = 0U; /* unknown */
457 0 : glob->clnt_send_fun( buf, buflen, addr, src_ip4_addr, glob->fun_arg );
458 0 : }
459 :
460 : static void
461 0 : fd_repair_send_requests( fd_repair_t * glob ) {
462 : /* Garbage collect old requests */
463 0 : long expire = glob->now - (long)5e9; /* 5 seconds */
464 0 : fd_repair_nonce_t n;
465 0 : for ( n = glob->oldest_nonce; n != glob->next_nonce; ++n ) {
466 0 : fd_needed_elem_t * ele = fd_needed_table_query( glob->needed, &n, NULL );
467 0 : if ( NULL == ele )
468 0 : continue;
469 0 : if (ele->when > expire)
470 0 : break;
471 : // (*glob->deliver_fail_fun)( &ele->key, ele->slot, ele->shred_index, glob->fun_arg, FD_REPAIR_DELIVER_FAIL_TIMEOUT );
472 0 : fd_dupdetect_elem_t * dup = fd_dupdetect_table_query( glob->dupdetect, &ele->dupkey, NULL );
473 0 : if( dup && --dup->req_cnt == 0) {
474 0 : fd_dupdetect_table_remove( glob->dupdetect, &ele->dupkey );
475 0 : }
476 0 : fd_needed_table_remove( glob->needed, &n );
477 0 : }
478 0 : glob->oldest_nonce = n;
479 :
480 : /* Send requests starting where we left off last time */
481 0 : if ( (int)(n - glob->current_nonce) < 0 )
482 0 : n = glob->current_nonce;
483 0 : ulong j = 0;
484 0 : ulong k = 0;
485 0 : for ( ; n != glob->next_nonce; ++n ) {
486 0 : ++k;
487 0 : fd_needed_elem_t * ele = fd_needed_table_query( glob->needed, &n, NULL );
488 0 : if ( NULL == ele )
489 0 : continue;
490 :
491 0 : if(j == 128U) break;
492 0 : ++j;
493 :
494 : /* Track statistics */
495 0 : ele->when = glob->now;
496 :
497 0 : fd_active_elem_t * active = fd_active_table_query( glob->actives, &ele->id, NULL );
498 0 : if ( active == NULL) {
499 0 : fd_dupdetect_elem_t * dup = fd_dupdetect_table_query( glob->dupdetect, &ele->dupkey, NULL );
500 0 : if( dup && --dup->req_cnt == 0) {
501 0 : fd_dupdetect_table_remove( glob->dupdetect, &ele->dupkey );
502 0 : }
503 0 : fd_needed_table_remove( glob->needed, &n );
504 0 : continue;
505 0 : }
506 :
507 0 : active->avg_reqs++;
508 0 : glob->metrics.send_pkt_cnt++;
509 :
510 0 : fd_repair_protocol_t protocol;
511 0 : switch (ele->dupkey.type) {
512 0 : case fd_needed_window_index: {
513 0 : glob->metrics.sent_pkt_types[FD_METRICS_ENUM_REPAIR_SENT_REQUEST_TYPES_V_NEEDED_WINDOW_IDX]++;
514 0 : fd_repair_protocol_new_disc(&protocol, fd_repair_protocol_enum_window_index);
515 0 : fd_repair_window_index_t * wi = &protocol.inner.window_index;
516 0 : fd_hash_copy(&wi->header.sender, glob->public_key);
517 0 : fd_hash_copy(&wi->header.recipient, &active->key);
518 0 : wi->header.timestamp = glob->now/1000000L;
519 0 : wi->header.nonce = n;
520 0 : wi->slot = ele->dupkey.slot;
521 0 : wi->shred_index = ele->dupkey.shred_index;
522 : // FD_LOG_INFO(("[repair]"))
523 0 : break;
524 0 : }
525 :
526 0 : case fd_needed_highest_window_index: {
527 0 : glob->metrics.sent_pkt_types[FD_METRICS_ENUM_REPAIR_SENT_REQUEST_TYPES_V_NEEDED_HIGHEST_WINDOW_IDX]++;
528 0 : fd_repair_protocol_new_disc(&protocol, fd_repair_protocol_enum_highest_window_index);
529 0 : fd_repair_highest_window_index_t * wi = &protocol.inner.highest_window_index;
530 0 : fd_hash_copy(&wi->header.sender, glob->public_key);
531 0 : fd_hash_copy(&wi->header.recipient, &active->key);
532 0 : wi->header.timestamp = glob->now/1000000L;
533 0 : wi->header.nonce = n;
534 0 : wi->slot = ele->dupkey.slot;
535 0 : wi->shred_index = ele->dupkey.shred_index;
536 0 : break;
537 0 : }
538 :
539 0 : case fd_needed_orphan: {
540 0 : glob->metrics.sent_pkt_types[FD_METRICS_ENUM_REPAIR_SENT_REQUEST_TYPES_V_NEEDED_ORPHAN_IDX]++;
541 0 : fd_repair_protocol_new_disc(&protocol, fd_repair_protocol_enum_orphan);
542 0 : fd_repair_orphan_t * wi = &protocol.inner.orphan;
543 0 : fd_hash_copy(&wi->header.sender, glob->public_key);
544 0 : fd_hash_copy(&wi->header.recipient, &active->key);
545 0 : wi->header.timestamp = glob->now/1000000L;
546 0 : wi->header.nonce = n;
547 0 : wi->slot = ele->dupkey.slot;
548 0 : break;
549 0 : }
550 0 : }
551 :
552 0 : fd_repair_sign_and_send( glob, &protocol, &active->addr );
553 :
554 0 : }
555 0 : glob->current_nonce = n;
556 0 : if( k )
557 0 : FD_LOG_DEBUG(("checked %lu nonces, sent %lu packets, total %lu", k, j, fd_needed_table_key_cnt( glob->needed )));
558 0 : }
559 :
560 : static void
561 0 : fd_repair_decay_stats( fd_repair_t * glob ) {
562 0 : for( fd_active_table_iter_t iter = fd_active_table_iter_init( glob->actives );
563 0 : !fd_active_table_iter_done( glob->actives, iter );
564 0 : iter = fd_active_table_iter_next( glob->actives, iter ) ) {
565 0 : fd_active_elem_t * ele = fd_active_table_iter_ele( glob->actives, iter );
566 0 : #define DECAY(_v_) _v_ = _v_ - ((_v_)>>3U) /* Reduce by 12.5% */
567 0 : DECAY(ele->avg_reqs);
568 0 : DECAY(ele->avg_reps);
569 0 : DECAY(ele->avg_lat);
570 0 : #undef DECAY
571 0 : }
572 0 : }
573 :
574 : /**
575 : * read_line() reads characters one by one from 'fd' until:
576 : * - it sees a newline ('\n')
577 : * - it reaches 'max_len - 1' characters
578 : * - or EOF (read returns 0)
579 : * It stores the line in 'buf' and null-terminates it.
580 : *
581 : * Returns the number of characters read (not counting the null terminator),
582 : * or -1 on error.
583 : */
584 0 : long read_line(int fd, char *buf) {
585 0 : long i = 0;
586 :
587 0 : while (i < 255) {
588 0 : char c;
589 0 : long n = read(fd, &c, 1);
590 :
591 0 : if (n < 0) {
592 0 : if (errno == EINTR) continue;
593 0 : return -1;
594 0 : } else if (n == 0) {
595 0 : break;
596 0 : }
597 :
598 0 : buf[i++] = c;
599 :
600 0 : if (c == '\n') {
601 0 : break;
602 0 : }
603 0 : }
604 :
605 0 : buf[i] = '\0';
606 0 : return i;
607 0 : }
608 :
609 : static int
610 0 : fd_read_in_good_peer_cache_file( fd_repair_t * repair ) {
611 0 : if( repair->good_peer_cache_file_fd==-1 ) {
612 0 : FD_LOG_NOTICE(( "No repair good_peer_cache_file specified, not loading cached peers" ));
613 0 : return 0;
614 0 : }
615 :
616 0 : long seek = lseek( repair->good_peer_cache_file_fd, 0UL, SEEK_SET );
617 0 : if( FD_UNLIKELY( seek!=0L ) ) {
618 0 : FD_LOG_WARNING(( "Failed to seek to the beginning of the good peer cache file" ));
619 0 : return 1;
620 0 : }
621 :
622 0 : int loaded_peers = 0;
623 0 : char line[256];
624 0 : char *saveptr = NULL;
625 :
626 0 : long len;
627 0 : while ((len = read_line(repair->good_peer_cache_file_fd, line)) > 0) {
628 :
629 : /* Strip newline if present */
630 0 : size_t len = strlen( line );
631 0 : if( len>0 && line[len-1]=='\n' ) {
632 0 : line[len-1] = '\0';
633 0 : len--;
634 0 : }
635 :
636 : /* Skip empty or comment lines */
637 0 : if( !len || line[0]=='#' ) continue;
638 :
639 : /* Parse: base58EncodedPubkey/ipAddr/port */
640 0 : char * base58_str = strtok_r( line, "/", &saveptr );
641 0 : char * ip_str = strtok_r( NULL, "/", &saveptr );
642 0 : char * port_str = strtok_r( NULL, "/", &saveptr );
643 :
644 0 : if( FD_UNLIKELY( !base58_str || !ip_str || !port_str ) ) {
645 0 : FD_LOG_WARNING(( "Malformed line, skipping" ));
646 0 : continue;
647 0 : }
648 :
649 : /* Decode the base58 public key */
650 0 : fd_pubkey_t pubkey;
651 0 : if( !fd_base58_decode_32( base58_str, pubkey.uc ) ) {
652 0 : FD_LOG_WARNING(( "Failed to decode base58 public key '%s', skipping", base58_str ));
653 0 : continue;
654 0 : }
655 :
656 : /* Convert IP address */
657 0 : struct in_addr addr_parsed;
658 0 : if( inet_aton( ip_str, &addr_parsed )==0 ) {
659 0 : FD_LOG_WARNING(( "Invalid IPv4 address '%s', skipping", ip_str ));
660 0 : continue;
661 0 : }
662 0 : uint ip_addr = (uint)addr_parsed.s_addr;
663 :
664 : /* Convert the port */
665 0 : char * endptr = NULL;
666 0 : long port = strtol( port_str, &endptr, 10 );
667 0 : if( (port<=0L) || (port>65535L) || (endptr && *endptr!='\0') ) {
668 0 : FD_LOG_WARNING(( "Invalid port '%s', skipping", port_str ));
669 0 : continue;
670 0 : }
671 :
672 : /* Create the peer address struct (byte-swap the port to network order). */
673 0 : fd_repair_peer_addr_t peer_addr;
674 : /* already in network byte order from inet_aton */
675 0 : peer_addr.addr = ip_addr;
676 : /* Flip to big-endian for network order */
677 0 : peer_addr.port = fd_ushort_bswap( (ushort)port );
678 :
679 : /* Add to active peers in the repair tile. */
680 0 : fd_repair_add_active_peer( repair, &peer_addr, &pubkey );
681 :
682 0 : loaded_peers++;
683 0 : }
684 :
685 0 : FD_LOG_INFO(( "Loaded %d peers from good peer cache file", loaded_peers ));
686 0 : return 0;
687 0 : }
688 :
689 : /* Start timed events and other protocol behavior */
690 : int
691 0 : fd_repair_start( fd_repair_t * glob ) {
692 0 : glob->last_sends = glob->now;
693 0 : glob->last_decay = glob->now;
694 0 : glob->last_print = glob->now;
695 0 : return fd_read_in_good_peer_cache_file( glob );
696 0 : }
697 :
698 : static void fd_repair_print_all_stats( fd_repair_t * glob );
699 : static void fd_actives_shuffle( fd_repair_t * repair );
700 : static int fd_write_good_peer_cache_file( fd_repair_t * repair );
701 :
702 : /* Dispatch timed events and other protocol behavior. This should be
703 : * called inside the main spin loop. */
704 : int
705 0 : fd_repair_continue( fd_repair_t * glob ) {
706 0 : fd_repair_lock( glob );
707 0 : if ( glob->now - glob->last_sends > (long)1e6 ) { /* 1 millisecond */
708 0 : fd_repair_send_requests( glob );
709 0 : glob->last_sends = glob->now;
710 0 : }
711 0 : if ( glob->now - glob->last_print > (long)30e9 ) { /* 30 seconds */
712 0 : fd_repair_print_all_stats( glob );
713 0 : glob->last_print = glob->now;
714 0 : fd_actives_shuffle( glob );
715 0 : fd_repair_decay_stats( glob );
716 0 : glob->last_decay = glob->now;
717 0 : } else if ( glob->now - glob->last_decay > (long)15e9 ) { /* 15 seconds */
718 0 : fd_actives_shuffle( glob );
719 0 : fd_repair_decay_stats( glob );
720 0 : glob->last_decay = glob->now;
721 0 : } else if ( glob->now - glob->last_good_peer_cache_file_write > (long)60e9 ) { /* 1 minute */
722 0 : fd_write_good_peer_cache_file( glob );
723 0 : glob->last_good_peer_cache_file_write = glob->now;
724 0 : }
725 0 : fd_repair_unlock( glob );
726 0 : return 0;
727 0 : }
728 :
729 : static void
730 : fd_repair_handle_ping( fd_repair_t * glob,
731 : fd_gossip_ping_t const * ping,
732 : fd_gossip_peer_addr_t const * peer_addr,
733 0 : uint self_ip4_addr ) {
734 0 : fd_repair_protocol_t protocol;
735 0 : fd_repair_protocol_new_disc(&protocol, fd_repair_protocol_enum_pong);
736 0 : fd_gossip_ping_t * pong = &protocol.inner.pong;
737 :
738 0 : fd_hash_copy( &pong->from, glob->public_key );
739 :
740 : /* Generate response hash token */
741 0 : uchar pre_image[FD_PING_PRE_IMAGE_SZ];
742 0 : memcpy( pre_image, "SOLANA_PING_PONG", 16UL );
743 0 : memcpy( pre_image+16UL, ping->token.uc, 32UL);
744 :
745 : /* Generate response hash token */
746 0 : fd_sha256_hash( pre_image, FD_PING_PRE_IMAGE_SZ, &pong->token );
747 :
748 : /* Sign it */
749 0 : (*glob->sign_fun)( glob->sign_arg, pong->signature.uc, pre_image, FD_PING_PRE_IMAGE_SZ, FD_KEYGUARD_SIGN_TYPE_SHA256_ED25519 );
750 :
751 0 : fd_bincode_encode_ctx_t ctx;
752 0 : uchar buf[1024];
753 0 : ctx.data = buf;
754 0 : ctx.dataend = buf + sizeof(buf);
755 0 : FD_TEST(0 == fd_repair_protocol_encode(&protocol, &ctx));
756 0 : ulong buflen = (ulong)((uchar*)ctx.data - buf);
757 :
758 0 : glob->clnt_send_fun( buf, buflen, peer_addr, self_ip4_addr, glob->fun_arg );
759 0 : }
760 :
761 : int
762 : fd_repair_recv_clnt_packet( fd_repair_t * glob,
763 : uchar const * msg,
764 : ulong msglen,
765 : fd_repair_peer_addr_t const * src_addr,
766 0 : uint dst_ip4_addr ) {
767 0 : glob->metrics.recv_clnt_pkt++;
768 :
769 0 : fd_repair_lock( glob );
770 0 : FD_SCRATCH_SCOPE_BEGIN {
771 0 : while( 1 ) {
772 0 : fd_bincode_decode_ctx_t ctx = {
773 0 : .data = msg,
774 0 : .dataend = msg + msglen
775 0 : };
776 :
777 0 : ulong total_sz = 0UL;
778 0 : if( FD_UNLIKELY( fd_repair_response_decode_footprint( &ctx, &total_sz ) ) ) {
779 : /* Solana falls back to assuming we got a shred in this case
780 : https://github.com/solana-labs/solana/blob/master/core/src/repair/serve_repair.rs#L1198 */
781 0 : break;
782 0 : }
783 :
784 0 : uchar * mem = fd_scratch_alloc( fd_repair_response_align(), total_sz );
785 0 : if( FD_UNLIKELY( !mem ) ) {
786 0 : FD_LOG_ERR(( "Unable to allocate memory for repair response" ));
787 0 : }
788 :
789 0 : fd_repair_response_t * gmsg = fd_repair_response_decode( mem, &ctx );
790 0 : if( FD_UNLIKELY( ctx.data != ctx.dataend ) ) {
791 0 : break;
792 0 : }
793 :
794 0 : switch( gmsg->discriminant ) {
795 0 : case fd_repair_response_enum_ping:
796 0 : fd_repair_handle_ping( glob, &gmsg->inner.ping, src_addr, dst_ip4_addr );
797 0 : break;
798 0 : }
799 :
800 0 : fd_repair_unlock( glob );
801 0 : return 0;
802 0 : }
803 :
804 : /* Look at the nonse */
805 0 : if( msglen < sizeof(fd_repair_nonce_t) ) {
806 0 : fd_repair_unlock( glob );
807 0 : return 0;
808 0 : }
809 0 : ulong shredlen = msglen - sizeof(fd_repair_nonce_t); /* Nonce is at the end */
810 0 : fd_repair_nonce_t key = *(fd_repair_nonce_t const *)(msg + shredlen);
811 0 : fd_needed_elem_t * val = fd_needed_table_query( glob->needed, &key, NULL );
812 0 : if( NULL == val ) {
813 0 : fd_repair_unlock( glob );
814 0 : return 0;
815 0 : }
816 :
817 0 : fd_active_elem_t * active = fd_active_table_query( glob->actives, &val->id, NULL );
818 0 : if( NULL != active ) {
819 : /* Update statistics */
820 0 : active->avg_reps++;
821 0 : active->avg_lat += glob->now - val->when;
822 0 : }
823 :
824 0 : fd_shred_t const * shred = fd_shred_parse(msg, shredlen);
825 0 : fd_repair_unlock( glob );
826 0 : if( shred == NULL ) {
827 0 : FD_LOG_WARNING(("invalid shread"));
828 0 : } else {
829 0 : glob->deliver_fun(shred, shredlen, src_addr, &val->id, glob->fun_arg);
830 0 : }
831 0 : } FD_SCRATCH_SCOPE_END;
832 0 : return 0;
833 0 : }
834 :
835 : int
836 0 : fd_repair_is_full( fd_repair_t * glob ) {
837 0 : return fd_needed_table_is_full(glob->needed);
838 0 : }
839 :
840 : /* Test if a peer is good. Returns 1 if the peer is "great", 0 if the peer is "good", and -1 if the peer sucks */
841 : static int
842 0 : is_good_peer( fd_active_elem_t * val ) {
843 0 : if( FD_UNLIKELY( NULL == val ) ) return -1; /* Very bad */
844 0 : if( val->avg_reqs > 10U && val->avg_reps == 0U ) return -1; /* Bad, no response after 10 requests */
845 0 : if( val->avg_reqs < 20U ) return 0; /* Not sure yet, good enough for now */
846 0 : if( (float)val->avg_reps < 0.01f*((float)val->avg_reqs) ) return -1; /* Very bad */
847 0 : if( (float)val->avg_reps < 0.8f*((float)val->avg_reqs) ) return 0; /* 80%, Good but not great */
848 0 : if( (float)val->avg_lat > 2500e9f*((float)val->avg_reps) ) return 0; /* 300ms, Good but not great */
849 0 : return 1; /* Great! */
850 0 : }
851 :
852 : #define SORT_NAME fd_latency_sort
853 0 : #define SORT_KEY_T long
854 0 : #define SORT_BEFORE(a,b) (a)<(b)
855 : #include "../../util/tmpl/fd_sort.c"
856 :
857 : static void
858 0 : fd_actives_shuffle( fd_repair_t * repair ) {
859 : /* Since we now have stake weights very quickly after reading the manifest, we wait
860 : until we have the stake weights before we start repairing. This ensures that we always
861 : sample from the available peers using stake weights. */
862 0 : if( repair->stake_weights_cnt == 0 ) {
863 0 : FD_LOG_NOTICE(( "repair does not have stake weights yet, not selecting any sticky peers" ));
864 0 : return;
865 0 : }
866 :
867 0 : FD_SCRATCH_SCOPE_BEGIN {
868 0 : ulong prev_sticky_cnt = repair->actives_sticky_cnt;
869 : /* Find all the usable stake holders */
870 0 : fd_active_elem_t ** leftovers = fd_scratch_alloc(
871 0 : alignof( fd_active_elem_t * ),
872 0 : sizeof( fd_active_elem_t * ) * repair->stake_weights_cnt );
873 0 : ulong leftovers_cnt = 0;
874 :
875 0 : ulong total_stake = 0UL;
876 0 : if( repair->stake_weights_cnt==0 ) {
877 0 : leftovers = fd_scratch_alloc(
878 0 : alignof( fd_active_elem_t * ),
879 0 : sizeof( fd_active_elem_t * ) * fd_active_table_key_cnt( repair->actives ) );
880 :
881 0 : for( fd_active_table_iter_t iter = fd_active_table_iter_init( repair->actives );
882 0 : !fd_active_table_iter_done( repair->actives, iter );
883 0 : iter = fd_active_table_iter_next( repair->actives, iter ) ) {
884 0 : fd_active_elem_t * peer = fd_active_table_iter_ele( repair->actives, iter );
885 0 : if( peer->sticky ) continue;
886 0 : leftovers[leftovers_cnt++] = peer;
887 0 : }
888 0 : } else {
889 0 : leftovers = fd_scratch_alloc(
890 0 : alignof( fd_active_elem_t * ),
891 0 : sizeof( fd_active_elem_t * ) * repair->stake_weights_cnt );
892 :
893 0 : for( ulong i = 0; i < repair->stake_weights_cnt; i++ ) {
894 0 : fd_stake_weight_t const * stake_weight = &repair->stake_weights[i];
895 0 : ulong stake = stake_weight->stake;
896 0 : if( !stake ) continue;
897 0 : fd_pubkey_t const * key = &stake_weight->key;
898 0 : fd_active_elem_t * peer = fd_active_table_query( repair->actives, key, NULL );
899 0 : if( peer!=NULL ) {
900 0 : peer->stake = stake;
901 0 : total_stake = fd_ulong_sat_add( total_stake, stake );
902 0 : }
903 0 : if( NULL == peer || peer->sticky ) continue;
904 0 : leftovers[leftovers_cnt++] = peer;
905 0 : }
906 0 : }
907 :
908 0 : fd_active_elem_t * best[FD_REPAIR_STICKY_MAX];
909 0 : ulong best_cnt = 0;
910 0 : fd_active_elem_t * good[FD_REPAIR_STICKY_MAX];
911 0 : ulong good_cnt = 0;
912 :
913 0 : long latencies[ FD_REPAIR_STICKY_MAX ];
914 0 : ulong latencies_cnt = 0UL;
915 :
916 0 : long first_quartile_latency = LONG_MAX;
917 :
918 : /* fetch all latencies */
919 0 : for( fd_active_table_iter_t iter = fd_active_table_iter_init( repair->actives );
920 0 : !fd_active_table_iter_done( repair->actives, iter );
921 0 : iter = fd_active_table_iter_next( repair->actives, iter ) ) {
922 0 : fd_active_elem_t * peer = fd_active_table_iter_ele( repair->actives, iter );
923 :
924 0 : if( !peer->sticky ) {
925 0 : continue;
926 0 : }
927 :
928 0 : if( peer->avg_lat==0L || peer->avg_reps==0UL ) {
929 0 : continue;
930 0 : }
931 :
932 0 : latencies[ latencies_cnt++ ] = peer->avg_lat/(long)peer->avg_reps;
933 0 : }
934 :
935 0 : if( latencies_cnt >= 4 ) {
936 : /* we probably want a few peers before sorting and pruning them based on
937 : latency. */
938 0 : fd_latency_sort_inplace( latencies, latencies_cnt );
939 0 : first_quartile_latency = latencies[ latencies_cnt / 4UL ];
940 0 : FD_LOG_NOTICE(( "repair peers first quartile latency - latency: %6.6f ms", (double)first_quartile_latency * 1e-6 ));
941 0 : }
942 :
943 : /* Build the new sticky peers set based on the latency and stake weight */
944 :
945 : /* select an upper bound */
946 : /* acceptable latency is 2 * first quartile latency */
947 0 : long acceptable_latency = first_quartile_latency != LONG_MAX ? 2L * first_quartile_latency : LONG_MAX;
948 0 : for( fd_active_table_iter_t iter = fd_active_table_iter_init( repair->actives );
949 0 : !fd_active_table_iter_done( repair->actives, iter );
950 0 : iter = fd_active_table_iter_next( repair->actives, iter ) ) {
951 0 : fd_active_elem_t * peer = fd_active_table_iter_ele( repair->actives, iter );
952 0 : uchar sticky = peer->sticky;
953 0 : peer->sticky = 0; /* Already clear the sticky bit */
954 0 : if( sticky ) {
955 : /* See if we still like this peer */
956 0 : if( peer->avg_reps>0UL && ( peer->avg_lat/(long)peer->avg_reps ) >= acceptable_latency ) {
957 0 : continue;
958 0 : }
959 0 : int r = is_good_peer( peer );
960 0 : if( r == 1 ) best[best_cnt++] = peer;
961 0 : else if( r == 0 ) good[good_cnt++] = peer;
962 0 : }
963 0 : }
964 :
965 0 : ulong tot_cnt = 0;
966 0 : for( ulong i = 0; i < best_cnt && tot_cnt < FD_REPAIR_STICKY_MAX - 2U; ++i ) {
967 0 : repair->actives_sticky[tot_cnt++] = best[i]->key;
968 0 : best[i]->sticky = (uchar)1;
969 0 : }
970 0 : for( ulong i = 0; i < good_cnt && tot_cnt < FD_REPAIR_STICKY_MAX - 2U; ++i ) {
971 0 : repair->actives_sticky[tot_cnt++] = good[i]->key;
972 0 : good[i]->sticky = (uchar)1;
973 0 : }
974 0 : if( leftovers_cnt ) {
975 : /* Sample 64 new sticky peers using stake-weighted sampling */
976 0 : for( ulong i = 0; i < 64 && tot_cnt < FD_REPAIR_STICKY_MAX && tot_cnt < fd_active_table_key_cnt( repair->actives ); ++i ) {
977 : /* Generate a random amount of culmative stake at which to sample the peer */
978 0 : ulong target_culm_stake = fd_rng_ulong( repair->rng ) % total_stake;
979 :
980 : /* Iterate over the active peers until we find the randomly selected peer */
981 0 : ulong culm_stake = 0UL;
982 0 : fd_active_elem_t * peer = NULL;
983 0 : for( fd_active_table_iter_t iter = fd_active_table_iter_init( repair->actives );
984 0 : !fd_active_table_iter_done( repair->actives, iter );
985 0 : iter = fd_active_table_iter_next( repair->actives, iter ) ) {
986 0 : peer = fd_active_table_iter_ele( repair->actives, iter );
987 0 : culm_stake = fd_ulong_sat_add( culm_stake, peer->stake );
988 0 : if( FD_UNLIKELY(( culm_stake >= target_culm_stake )) ) {
989 0 : break;
990 0 : }
991 0 : }
992 :
993 : /* Select this peer as sticky */
994 0 : if( FD_LIKELY(( peer && !peer->sticky )) ) {
995 0 : repair->actives_sticky[tot_cnt++] = peer->key;
996 0 : peer->sticky = (uchar)1;
997 0 : }
998 0 : }
999 :
1000 0 : }
1001 0 : repair->actives_sticky_cnt = tot_cnt;
1002 :
1003 0 : FD_LOG_NOTICE(
1004 0 : ( "selected %lu (previously: %lu) peers for repair (best was %lu, good was %lu, leftovers was %lu) (nonce_diff: %u)",
1005 0 : tot_cnt,
1006 0 : prev_sticky_cnt,
1007 0 : best_cnt,
1008 0 : good_cnt,
1009 0 : leftovers_cnt,
1010 0 : repair->next_nonce - repair->current_nonce ) );
1011 0 : }
1012 0 : FD_SCRATCH_SCOPE_END;
1013 0 : }
1014 :
1015 : static fd_active_elem_t *
1016 0 : actives_sample( fd_repair_t * repair ) {
1017 0 : ulong seed = repair->actives_random_seed;
1018 0 : ulong actives_sticky_cnt = repair->actives_sticky_cnt;
1019 0 : while( actives_sticky_cnt ) {
1020 0 : seed += 774583887101UL;
1021 0 : fd_pubkey_t * id = &repair->actives_sticky[seed % actives_sticky_cnt];
1022 0 : fd_active_elem_t * peer = fd_active_table_query( repair->actives, id, NULL );
1023 0 : if( NULL != peer ) {
1024 0 : if( peer->first_request_time == 0U ) peer->first_request_time = repair->now;
1025 : /* Aggressively throw away bad peers */
1026 0 : if( repair->now - peer->first_request_time < (long)5e9 || /* Sample the peer for at least 5 seconds */
1027 0 : is_good_peer( peer ) != -1 ) {
1028 0 : repair->actives_random_seed = seed;
1029 0 : return peer;
1030 0 : }
1031 0 : peer->sticky = 0;
1032 0 : }
1033 0 : *id = repair->actives_sticky[--( actives_sticky_cnt )];
1034 0 : }
1035 0 : return NULL;
1036 0 : }
1037 :
1038 : static int
1039 0 : fd_repair_create_needed_request( fd_repair_t * glob, int type, ulong slot, uint shred_index ) {
1040 0 : fd_repair_lock( glob );
1041 :
1042 : /* If there are no active sticky peers from which to send requests to, refresh the sticky peers
1043 : selection. It may be that stake weights were not available before, and now they are. */
1044 0 : if ( glob->actives_sticky_cnt == 0 ) {
1045 0 : fd_actives_shuffle( glob );
1046 0 : }
1047 :
1048 0 : fd_pubkey_t * ids[FD_REPAIR_NUM_NEEDED_PEERS] = {0};
1049 0 : uint found_peer = 0;
1050 0 : uint peer_cnt = fd_uint_min( (uint)glob->actives_sticky_cnt, FD_REPAIR_NUM_NEEDED_PEERS );
1051 0 : for( ulong i=0UL; i<peer_cnt; i++ ) {
1052 0 : fd_active_elem_t * peer = actives_sample( glob );
1053 0 : if(!peer) continue;
1054 0 : found_peer = 1;
1055 :
1056 0 : ids[i] = &peer->key;
1057 0 : }
1058 :
1059 0 : if (!found_peer) {
1060 0 : FD_LOG_DEBUG( ( "failed to find a good peer." ) );
1061 0 : fd_repair_unlock( glob );
1062 0 : return -1;
1063 0 : };
1064 :
1065 0 : fd_dupdetect_key_t dupkey = { .type = (enum fd_needed_elem_type)type, .slot = slot, .shred_index = shred_index };
1066 0 : fd_dupdetect_elem_t * dupelem = fd_dupdetect_table_query( glob->dupdetect, &dupkey, NULL );
1067 0 : if( dupelem == NULL ) {
1068 0 : dupelem = fd_dupdetect_table_insert( glob->dupdetect, &dupkey );
1069 0 : dupelem->last_send_time = 0L;
1070 0 : } else if( ( dupelem->last_send_time+(long)200e6 )<glob->now ) {
1071 0 : fd_repair_unlock( glob );
1072 0 : return 0;
1073 0 : }
1074 :
1075 0 : dupelem->last_send_time = glob->now;
1076 0 : dupelem->req_cnt = peer_cnt;
1077 :
1078 0 : if (fd_needed_table_is_full(glob->needed)) {
1079 0 : fd_repair_unlock( glob );
1080 0 : FD_LOG_NOTICE(("table full"));
1081 0 : ( *glob->deliver_fail_fun )(ids[0], slot, shred_index, glob->fun_arg, FD_REPAIR_DELIVER_FAIL_REQ_LIMIT_EXCEEDED );
1082 0 : return -1;
1083 0 : }
1084 0 : for( ulong i=0UL; i<peer_cnt; i++ ) {
1085 0 : fd_repair_nonce_t key = glob->next_nonce++;
1086 0 : fd_needed_elem_t * val = fd_needed_table_insert(glob->needed, &key);
1087 0 : fd_hash_copy(&val->id, ids[i]);
1088 0 : val->dupkey = dupkey;
1089 0 : val->when = glob->now;
1090 0 : }
1091 0 : fd_repair_unlock( glob );
1092 0 : return 0;
1093 0 : }
1094 :
1095 : static int
1096 0 : fd_write_good_peer_cache_file( fd_repair_t * repair ) {
1097 : // return 0;
1098 :
1099 0 : if ( repair->good_peer_cache_file_fd == -1 ) {
1100 0 : return 0;
1101 0 : }
1102 :
1103 0 : if ( repair->actives_sticky_cnt == 0 ) {
1104 0 : return 0;
1105 0 : }
1106 :
1107 : /* Truncate the file before we write it */
1108 0 : int err = ftruncate( repair->good_peer_cache_file_fd, 0UL );
1109 0 : if( FD_UNLIKELY( err==-1 ) ) {
1110 0 : FD_LOG_WARNING(( "Failed to truncate the good peer cache file (%i-%s)", errno, fd_io_strerror( errno ) ));
1111 0 : return 1;
1112 0 : }
1113 0 : long seek = lseek( repair->good_peer_cache_file_fd, 0UL, SEEK_SET );
1114 0 : if( FD_UNLIKELY( seek!=0L ) ) {
1115 0 : FD_LOG_WARNING(( "Failed to seek to the beginning of the good peer cache file" ));
1116 0 : return 1;
1117 0 : }
1118 :
1119 : /* Write the active sticky peers to file in the format:
1120 : "base58EncodedPubkey/ipAddr/port"
1121 :
1122 : Where ipAddr is in dotted-decimal (e.g. "1.2.3.4")
1123 : and port is decimal, in host order (e.g. "8001").
1124 : */
1125 0 : for( ulong i = 0UL; i < repair->actives_sticky_cnt; i++ ) {
1126 0 : fd_pubkey_t * id = &repair->actives_sticky[ i ];
1127 0 : fd_active_elem_t * peer = fd_active_table_query( repair->actives, id, NULL );
1128 0 : if ( peer == NULL ) {
1129 0 : continue;
1130 0 : }
1131 :
1132 : /* Convert the public key to base58 */
1133 0 : char base58_str[ FD_BASE58_ENCODED_32_SZ ];
1134 0 : fd_base58_encode_32( peer->key.uc, NULL, base58_str );
1135 :
1136 : /* Convert the IP address to dotted-decimal string. The address
1137 : in peer->addr.addr is already in network byte order. */
1138 0 : struct in_addr addr_parsed;
1139 0 : addr_parsed.s_addr = peer->addr.addr; /* net-order -> struct in_addr */
1140 0 : char * ip_str = inet_ntoa( addr_parsed );
1141 :
1142 : /* Convert port from network byte order to host byte order. */
1143 0 : ushort port = fd_ushort_bswap( peer->addr.port );
1144 :
1145 : /* Write out line: base58EncodedPubkey/ipAddr/port */
1146 0 : dprintf( repair->good_peer_cache_file_fd, "%s/%s/%u\n", base58_str, ip_str, (uint)port );
1147 0 : }
1148 :
1149 0 : return 0;
1150 0 : }
1151 :
1152 : int
1153 0 : fd_repair_need_window_index( fd_repair_t * glob, ulong slot, uint shred_index ) {
1154 : // FD_LOG_NOTICE(( "[repair] need window %lu, shred_index %u", slot, shred_index ));
1155 0 : return fd_repair_create_needed_request( glob, fd_needed_window_index, slot, shred_index );
1156 0 : }
1157 :
1158 : int
1159 0 : fd_repair_need_highest_window_index( fd_repair_t * glob, ulong slot, uint shred_index ) {
1160 : // FD_LOG_NOTICE(( "[repair] need highest %lu", slot ));
1161 0 : return fd_repair_create_needed_request( glob, fd_needed_highest_window_index, slot, shred_index );
1162 0 : }
1163 :
1164 : int
1165 0 : fd_repair_need_orphan( fd_repair_t * glob, ulong slot ) {
1166 0 : FD_LOG_NOTICE( ( "[repair] need orphan %lu", slot ) );
1167 0 : return fd_repair_create_needed_request( glob, fd_needed_orphan, slot, UINT_MAX );
1168 0 : }
1169 :
1170 : static void
1171 0 : print_stats( fd_active_elem_t * val ) {
1172 0 : fd_pubkey_t const * id = &val->key;
1173 0 : if( FD_UNLIKELY( NULL == val ) ) return;
1174 0 : if( val->avg_reqs == 0 )
1175 0 : FD_LOG_DEBUG(( "repair peer %s: no requests sent, stake=%lu", FD_BASE58_ENC_32_ALLOCA( id ), val->stake / (ulong)1e9 ));
1176 0 : else if( val->avg_reps == 0 )
1177 0 : FD_LOG_DEBUG(( "repair peer %s: avg_requests=%lu, no responses received, stake=%lu", FD_BASE58_ENC_32_ALLOCA( id ), val->avg_reqs, val->stake / (ulong)1e9 ));
1178 0 : else
1179 0 : FD_LOG_DEBUG(( "repair peer %s: avg_requests=%lu, response_rate=%f, latency=%f, stake=%lu",
1180 0 : FD_BASE58_ENC_32_ALLOCA( id ),
1181 0 : val->avg_reqs,
1182 0 : ((double)val->avg_reps)/((double)val->avg_reqs),
1183 0 : 1.0e-9*((double)val->avg_lat)/((double)val->avg_reps),
1184 0 : val->stake / (ulong)1e9 ));
1185 0 : }
1186 :
1187 : static void
1188 0 : fd_repair_print_all_stats( fd_repair_t * glob ) {
1189 0 : for( fd_active_table_iter_t iter = fd_active_table_iter_init( glob->actives );
1190 0 : !fd_active_table_iter_done( glob->actives, iter );
1191 0 : iter = fd_active_table_iter_next( glob->actives, iter ) ) {
1192 0 : fd_active_elem_t * val = fd_active_table_iter_ele( glob->actives, iter );
1193 0 : if( !val->sticky ) continue;
1194 0 : print_stats( val );
1195 0 : }
1196 0 : FD_LOG_INFO( ( "peer count: %lu", fd_active_table_key_cnt( glob->actives ) ) );
1197 0 : }
1198 :
1199 0 : void fd_repair_add_sticky( fd_repair_t * glob, fd_pubkey_t const * id ) {
1200 0 : fd_repair_lock( glob );
1201 0 : glob->actives_sticky[glob->actives_sticky_cnt++] = *id;
1202 0 : fd_repair_unlock( glob );
1203 0 : }
1204 :
1205 : void
1206 : fd_repair_set_stake_weights( fd_repair_t * repair,
1207 : fd_stake_weight_t const * stake_weights,
1208 0 : ulong stake_weights_cnt ) {
1209 0 : if( stake_weights == NULL ) {
1210 0 : FD_LOG_ERR(( "stake weights NULL" ));
1211 0 : }
1212 0 : if( stake_weights_cnt > FD_STAKE_WEIGHTS_MAX ) {
1213 0 : FD_LOG_ERR(( "too many stake weights" ));
1214 0 : }
1215 :
1216 0 : fd_repair_lock( repair );
1217 :
1218 0 : fd_memset( repair->stake_weights, 0, FD_STAKE_WEIGHTS_MAX * fd_stake_weight_footprint() );
1219 0 : fd_memcpy( repair->stake_weights, stake_weights, stake_weights_cnt * sizeof(fd_stake_weight_t) );
1220 0 : repair->stake_weights_cnt = stake_weights_cnt;
1221 :
1222 0 : fd_repair_unlock( repair );
1223 0 : }
1224 :
1225 : static void
1226 : fd_repair_send_ping( fd_repair_t * glob,
1227 : fd_gossip_peer_addr_t const * dst_addr,
1228 : uint src_ip4_addr,
1229 0 : fd_pinged_elem_t * val ) {
1230 0 : fd_repair_response_t gmsg;
1231 0 : fd_repair_response_new_disc( &gmsg, fd_repair_response_enum_ping );
1232 0 : fd_gossip_ping_t * ping = &gmsg.inner.ping;
1233 0 : fd_hash_copy( &ping->from, glob->public_key );
1234 :
1235 0 : uchar pre_image[FD_PING_PRE_IMAGE_SZ];
1236 0 : memcpy( pre_image, "SOLANA_PING_PONG", 16UL );
1237 0 : memcpy( pre_image+16UL, val->token.uc, 32UL );
1238 :
1239 0 : fd_sha256_hash( pre_image, FD_PING_PRE_IMAGE_SZ, &ping->token );
1240 :
1241 0 : glob->sign_fun( glob->sign_arg, ping->signature.uc, pre_image, FD_PING_PRE_IMAGE_SZ, FD_KEYGUARD_SIGN_TYPE_SHA256_ED25519 );
1242 :
1243 0 : fd_bincode_encode_ctx_t ctx;
1244 0 : uchar buf[1024];
1245 0 : ctx.data = buf;
1246 0 : ctx.dataend = buf + sizeof(buf);
1247 0 : FD_TEST(0 == fd_repair_response_encode(&gmsg, &ctx));
1248 0 : ulong buflen = (ulong)((uchar*)ctx.data - buf);
1249 :
1250 0 : glob->serv_send_fun( buf, buflen, dst_addr, src_ip4_addr, glob->fun_arg );
1251 0 : }
1252 :
1253 : static void
1254 0 : fd_repair_recv_pong(fd_repair_t * glob, fd_gossip_ping_t const * pong, fd_gossip_peer_addr_t const * from) {
1255 0 : fd_pinged_elem_t * val = fd_pinged_table_query(glob->pinged, from, NULL);
1256 0 : if( val == NULL || !fd_hash_eq( &val->id, &pong->from ) )
1257 0 : return;
1258 :
1259 : /* Verify response hash token */
1260 0 : uchar pre_image[FD_PING_PRE_IMAGE_SZ];
1261 0 : memcpy( pre_image, "SOLANA_PING_PONG", 16UL );
1262 0 : memcpy( pre_image+16UL, val->token.uc, 32UL );
1263 :
1264 0 : fd_hash_t pre_image_hash;
1265 0 : fd_sha256_hash( pre_image, FD_PING_PRE_IMAGE_SZ, pre_image_hash.uc );
1266 :
1267 0 : fd_sha256_t sha[1];
1268 0 : fd_sha256_init( sha );
1269 0 : fd_sha256_append( sha, "SOLANA_PING_PONG", 16UL );
1270 0 : fd_sha256_append( sha, pre_image_hash.uc, 32UL );
1271 0 : fd_hash_t golden;
1272 0 : fd_sha256_fini( sha, golden.uc );
1273 :
1274 0 : fd_sha512_t sha2[1];
1275 0 : if( fd_ed25519_verify( /* msg */ golden.uc,
1276 0 : /* sz */ 32U,
1277 0 : /* sig */ pong->signature.uc,
1278 0 : /* public_key */ pong->from.uc,
1279 0 : sha2 )) {
1280 0 : FD_LOG_WARNING(("Failed sig verify for pong"));
1281 0 : return;
1282 0 : }
1283 :
1284 0 : val->good = 1;
1285 0 : }
1286 :
1287 : int
1288 : fd_repair_recv_serv_packet( fd_repair_t * glob,
1289 : uchar const * msg,
1290 : ulong msglen,
1291 : fd_repair_peer_addr_t const * peer_addr,
1292 0 : uint self_ip4_addr ) {
1293 : //ulong recv_serv_packet;
1294 : //ulong recv_serv_pkt_types[FD_METRICS_ENUM_SENT_REQUEST_TYPES_CNT];
1295 :
1296 0 : FD_SCRATCH_SCOPE_BEGIN {
1297 0 : fd_bincode_decode_ctx_t ctx = {
1298 0 : .data = msg,
1299 0 : .dataend = msg + msglen
1300 0 : };
1301 :
1302 0 : ulong total_sz = 0UL;
1303 0 : if( FD_UNLIKELY( fd_repair_protocol_decode_footprint( &ctx, &total_sz ) ) ) {
1304 0 : glob->metrics.recv_serv_corrupt_pkt++;
1305 0 : FD_LOG_WARNING(( "Failed to decode repair request packet" ));
1306 0 : return 0;
1307 0 : }
1308 :
1309 0 : glob->metrics.recv_serv_pkt++;
1310 :
1311 0 : uchar * mem = fd_scratch_alloc( fd_repair_protocol_align(), total_sz );
1312 0 : if( FD_UNLIKELY( !mem ) ) {
1313 0 : FD_LOG_ERR(( "Unable to allocate memory for repair protocol" ));
1314 0 : }
1315 :
1316 0 : fd_repair_protocol_t * protocol = fd_repair_protocol_decode( mem, &ctx );
1317 :
1318 0 : if( FD_UNLIKELY( ctx.data != ctx.dataend ) ) {
1319 0 : FD_LOG_WARNING(( "failed to decode repair request packet" ));
1320 0 : return 0;
1321 0 : }
1322 :
1323 0 : fd_repair_request_header_t * header;
1324 0 : switch( protocol->discriminant ) {
1325 0 : case fd_repair_protocol_enum_pong:
1326 0 : glob->metrics.recv_serv_pkt_types[FD_METRICS_ENUM_REPAIR_SERV_PKT_TYPES_V_PONG_IDX]++;
1327 0 : fd_repair_lock( glob );
1328 0 : fd_repair_recv_pong( glob, &protocol->inner.pong, peer_addr );
1329 0 : fd_repair_unlock( glob );
1330 0 : return 0;
1331 0 : case fd_repair_protocol_enum_window_index: {
1332 0 : glob->metrics.recv_serv_pkt_types[FD_METRICS_ENUM_REPAIR_SERV_PKT_TYPES_V_WINDOW_IDX]++;
1333 0 : fd_repair_window_index_t * wi = &protocol->inner.window_index;
1334 0 : header = &wi->header;
1335 0 : break;
1336 0 : }
1337 0 : case fd_repair_protocol_enum_highest_window_index: {
1338 0 : glob->metrics.recv_serv_pkt_types[FD_METRICS_ENUM_REPAIR_SERV_PKT_TYPES_V_HIGHEST_WINDOW_IDX]++;
1339 0 : fd_repair_highest_window_index_t * wi = &protocol->inner.highest_window_index;
1340 0 : header = &wi->header;
1341 0 : break;
1342 0 : }
1343 0 : case fd_repair_protocol_enum_orphan: {
1344 0 : glob->metrics.recv_serv_pkt_types[FD_METRICS_ENUM_REPAIR_SERV_PKT_TYPES_V_ORPHAN_IDX]++;
1345 0 : fd_repair_orphan_t * wi = &protocol->inner.orphan;
1346 0 : header = &wi->header;
1347 0 : break;
1348 0 : }
1349 0 : default: {
1350 0 : glob->metrics.recv_serv_pkt_types[FD_METRICS_ENUM_REPAIR_SERV_PKT_TYPES_V_UNKNOWN_IDX]++;
1351 0 : FD_LOG_WARNING(( "received repair request of unknown type: %d", (int)protocol->discriminant ));
1352 0 : return 0;
1353 0 : }
1354 0 : }
1355 :
1356 0 : if( FD_UNLIKELY( !fd_hash_eq( &header->recipient, glob->public_key ) ) ) {
1357 0 : FD_LOG_WARNING(( "received repair request with wrong recipient, %s instead of %s", FD_BASE58_ENC_32_ALLOCA( header->recipient.uc ), FD_BASE58_ENC_32_ALLOCA( glob->public_key ) ));
1358 0 : return 0;
1359 0 : }
1360 :
1361 : /* Verify the signature */
1362 0 : fd_sha512_t sha2[1];
1363 0 : fd_signature_t sig;
1364 0 : fd_memcpy( &sig, header->signature.uc, sizeof(sig) );
1365 0 : fd_memcpy( (uchar *)msg + 64U, msg, 4U );
1366 0 : if( fd_ed25519_verify( /* msg */ msg + 64U,
1367 0 : /* sz */ msglen - 64U,
1368 0 : /* sig */ sig.uc,
1369 0 : /* public_key */ header->sender.uc,
1370 0 : sha2 )) {
1371 0 : glob->metrics.recv_serv_invalid_signature++;
1372 0 : FD_LOG_WARNING(( "received repair request with with invalid signature" ));
1373 0 : return 0;
1374 0 : }
1375 :
1376 0 : fd_repair_lock( glob );
1377 :
1378 0 : fd_pinged_elem_t * val = fd_pinged_table_query( glob->pinged, peer_addr, NULL) ;
1379 0 : if( val == NULL || !val->good || !fd_hash_eq( &val->id, &header->sender ) ) {
1380 : /* Need to ping this client */
1381 0 : if( val == NULL ) {
1382 0 : if( fd_pinged_table_is_full( glob->pinged ) ) {
1383 0 : FD_LOG_WARNING(( "pinged table is full" ));
1384 0 : fd_repair_unlock( glob );
1385 0 : glob->metrics.recv_serv_full_ping_table++;
1386 0 : return 0;
1387 0 : }
1388 0 : val = fd_pinged_table_insert( glob->pinged, peer_addr );
1389 0 : for ( ulong i = 0; i < FD_HASH_FOOTPRINT / sizeof(ulong); i++ )
1390 0 : val->token.ul[i] = fd_rng_ulong(glob->rng);
1391 0 : }
1392 0 : fd_hash_copy( &val->id, &header->sender );
1393 0 : val->good = 0;
1394 0 : fd_repair_send_ping( glob, peer_addr, self_ip4_addr, val );
1395 :
1396 0 : } else {
1397 0 : uchar buf[FD_SHRED_MAX_SZ + sizeof(uint)];
1398 0 : switch( protocol->discriminant ) {
1399 0 : case fd_repair_protocol_enum_window_index: {
1400 0 : fd_repair_window_index_t const * wi = &protocol->inner.window_index;
1401 0 : long sz = (*glob->serv_get_shred_fun)( wi->slot, (uint)wi->shred_index, buf, FD_SHRED_MAX_SZ, glob->fun_arg );
1402 0 : if( sz < 0 ) break;
1403 0 : *(uint *)(buf + sz) = wi->header.nonce;
1404 0 : glob->serv_send_fun( buf, (ulong)sz + sizeof(uint), peer_addr, self_ip4_addr, glob->fun_arg );
1405 0 : break;
1406 0 : }
1407 :
1408 0 : case fd_repair_protocol_enum_highest_window_index: {
1409 0 : fd_repair_highest_window_index_t const * wi = &protocol->inner.highest_window_index;
1410 0 : long sz = (*glob->serv_get_shred_fun)( wi->slot, UINT_MAX, buf, FD_SHRED_MAX_SZ, glob->fun_arg );
1411 0 : if( sz < 0 ) break;
1412 0 : *(uint *)(buf + sz) = wi->header.nonce;
1413 0 : glob->serv_send_fun( buf, (ulong)sz + sizeof(uint), peer_addr, self_ip4_addr, glob->fun_arg );
1414 0 : break;
1415 0 : }
1416 :
1417 0 : case fd_repair_protocol_enum_orphan: {
1418 0 : fd_repair_orphan_t const * wi = &protocol->inner.orphan;
1419 0 : ulong slot = wi->slot;
1420 0 : for(unsigned i = 0; i < 10; ++i) {
1421 0 : slot = (*glob->serv_get_parent_fun)( slot, glob->fun_arg );
1422 : /* We cannot serve slots <= 1 since they are empy and created at genesis. */
1423 0 : if( slot == FD_SLOT_NULL || slot <= 1UL ) break;
1424 0 : long sz = (*glob->serv_get_shred_fun)( slot, UINT_MAX, buf, FD_SHRED_MAX_SZ, glob->fun_arg );
1425 0 : if( sz < 0 ) continue;
1426 0 : *(uint *)(buf + sz) = wi->header.nonce;
1427 0 : glob->serv_send_fun( buf, (ulong)sz + sizeof(uint), peer_addr, self_ip4_addr, glob->fun_arg );
1428 0 : }
1429 0 : break;
1430 0 : }
1431 :
1432 0 : default:
1433 0 : break;
1434 0 : }
1435 0 : }
1436 :
1437 0 : fd_repair_unlock( glob );
1438 0 : } FD_SCRATCH_SCOPE_END;
1439 0 : return 0;
1440 0 : }
1441 :
1442 : fd_repair_metrics_t *
1443 0 : fd_repair_get_metrics( fd_repair_t * repair ) {
1444 0 : return &repair->metrics;
1445 0 : }
|