LCOV - code coverage report
Current view: top level - flamenco/repair - fd_repair.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 951 0.0 %
Date: 2025-03-20 12:08:36 Functions: 0 52 0.0 %

          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 : }

Generated by: LCOV version 1.14