LCOV - code coverage report
Current view: top level - disco/repair - fd_repair_tile.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 1144 0.0 %
Date: 2026-11-11 12:40:43 Functions: 0 29 0.0 %

          Line data    Source code
       1             : #define _GNU_SOURCE
       2             : 
       3             : #include "../../disco/topo/fd_topo.h"
       4             : #include "../../disco/fd_clock_tile.h"
       5             : #include <linux/futex.h>
       6             : #include "generated/fd_repair_tile_seccomp.h"
       7             : #include "../../disco/shred/fd_shred_tile.h"
       8             : #include "fd_repair.h"
       9             : #include "../../choreo/votor/fd_cert.h"
      10             : #include "../../discof/genesis/fd_genesi_tile.h"
      11             : #include "../../discof/restore/utils/fd_ssmsg.h"
      12             : #include "../../discof/votor/fd_votor_tile.h"
      13             : #include "../../discof/replay/fd_replay_tile.h"
      14             : #include "../../disco/keyguard/fd_keyload.h"
      15             : #include "../../disco/keyguard/fd_keyswitch.h"
      16             : #include "../../flamenco/gossip/fd_gossip_message.h"
      17             : #include "../../flamenco/leaders/fd_leaders_base.h"
      18             : #include "../../flamenco/leaders/fd_multi_epoch_leaders.h"
      19             : #include "fd_repair_serde.h"
      20             : #include "fd_repair_strat.h"
      21             : #include "../../ballet/sha256/fd_sha256.h"
      22             : #include "../../ballet/chacha/fd_chacha_rng.h"
      23             : #include "../../disco/keyguard/fd_keyguard.h"
      24             : #include "../../disco/events/generated/fd_event_gen.h"
      25             : #include "../../disco/metrics/fd_metrics.h"
      26             : #include "../../disco/net/fd_net_tile.h"
      27             : #include "../../disco/shred/fd_rnonce_ss.h"
      28             : #include "../../util/net/fd_net_headers.h"
      29             : #include "../../util/pod/fd_pod_format.h"
      30             : #include "fd_repair_tile.h"
      31             : 
      32             : /* timeout: tracks when a repair request has timed out and it's time to
      33             :    try again. */
      34             : 
      35             : struct timeout {
      36             :   long   timeout;   /* ns */
      37             :   ulong  slot;
      38             :   ulong  blk;       /* blk_pool idx, checked against slot when due */
      39             :   uint   fec_idx;   /* FEC index, WINDOW_INDEX only */
      40             :   uchar  tag;       /* FD_REPAIR_SERDE_TAG_*, WINDOW_INDEX for any FEC set */
      41             :   uchar  attempt;
      42             :   ushort last_peer; /* 1+strat slot of the peer last asked, 0 if none */
      43             :   uint   rcvd;      /* the FEC set's data_rcvd at the last attempt, UINT_MAX once complete */
      44             :   uint   stuck;     /* attempts in a row that brought no new shred */
      45             : };
      46             : typedef struct timeout timeout_t;
      47             : FD_STATIC_ASSERT( FD_REPAIR_STRAT_PEER_MAX<USHORT_MAX, last_peer );
      48             : 
      49             : #define PRQ_NAME    timeout_prq
      50           0 : #define PRQ_T       timeout_t
      51             : #include "../../util/tmpl/fd_prq.c"
      52             : 
      53             : /* pending: an outstanding request we're waiting for to either receive a
      54             :    response or get timed out.  one-to-many relationship with timeout
      55             :    (there can be many pendings per timeout). */
      56             : 
      57           0 : #define PENDING_TTL ((long)FD_RNONCE_SS_DELTA_MAX<<24) /* a shred reply's nonce stops verifying after this */
      58           0 : #define SIGN_NS     (6500L)                            /* fd_ed25519_sign of a request, test_ed25519 --bench */
      59           0 : #define FEC_SET_P90 (38UL)                             /* p90 fec_set_cnt */
      60             : 
      61             : struct pending {
      62             :   ulong key;
      63             :   ulong next; /* pool next */
      64             :   struct {
      65             :     ulong prev;
      66             :     ulong next;
      67             :   } map;
      68             :   struct {
      69             :     ulong prev;
      70             :     ulong next;
      71             :   } dlist;
      72             :   long        ts;          /* sent timestamp, nanos */
      73             :   ulong       slot;
      74             :   ulong       blk;         /* blk_pool idx */
      75             :   fd_hash_t   dmr;
      76             :   fd_pubkey_t peer;        /* id_key of the peer picked */
      77             :   uint        fec_idx;     /* FEC index */
      78             :   uint        fec_set_cnt; /* the blk's when sent */
      79             :   uint        tag;         /* FD_REPAIR_SERDE_TAG_* of the request */
      80             :   uint        mask;        /* bit i set while shred i of the FEC set is unanswered, bit 0 for a HIGHEST or ORPHAN; the pending outlives its last answer */
      81             :   uint        answered;    /* shreds matched so far */
      82             :   uint        ip4;
      83             :   ushort      port;
      84             :   uchar       attempt;
      85             :   uchar       pick;
      86             :   uchar       role;
      87             : };
      88             : typedef struct pending pending_t;
      89             : 
      90             : #define POOL_NAME pending_pool
      91           0 : #define POOL_T    pending_t
      92             : #include "../../util/tmpl/fd_pool.c"
      93             : 
      94             : #define MAP_NAME                           pending_map
      95           0 : #define MAP_ELE_T                          pending_t
      96           0 : #define MAP_PREV                           map.prev
      97           0 : #define MAP_NEXT                           map.next
      98             : #define MAP_MULTI                          1
      99             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
     100             : #include "../../util/tmpl/fd_map_chain.c"
     101             : 
     102             : #define DLIST_NAME  pending_dlist
     103             : #define DLIST_ELE_T pending_t
     104           0 : #define DLIST_PREV  dlist.prev
     105           0 : #define DLIST_NEXT  dlist.next
     106             : #include "../../util/tmpl/fd_dlist.c"
     107             : 
     108             : #define SIGN_MAX     (16UL)
     109           0 : #define SIGN_RESERVE (8UL)  /* sign slots only pongs may take */
     110           0 : #define PING_MAX     (4UL)  /* pings per after_credit */
     111             : 
     112             : struct sign {
     113             :   ulong  next;
     114             :   uchar  buf[ FD_REPAIR_SER_MAX ];
     115             :   ulong  sz;
     116             :   ulong  sig_off; /* where the signature goes in buf */
     117             :   uint   saddr;   /* 0 lets the net tile choose */
     118             :   uint   daddr;
     119             :   ushort dport;   /* net order */
     120             : };
     121             : typedef struct sign sign_t;
     122             : 
     123             : #define POOL_NAME sign_pool
     124           0 : #define POOL_T    sign_t
     125             : #include "../../util/tmpl/fd_pool.c"
     126             : 
     127             : #define DEQUE_NAME ping_deque
     128           0 : #define DEQUE_T    fd_pubkey_t
     129             : #include "../../util/tmpl/fd_deque_dynamic.c"
     130             : 
     131           0 : #define PING_TTL  (500L*1000000L)   /* a ping is answered this long after we sent its peer a request, Agave's 2*DELTA */
     132           0 : #define BAN_TTL   (10L*1000000000L) /* a peer whose response fails to verify is not picked for this long */
     133           0 : #define STUCK_MAX (3U)              /* attempts with no new shred before a retry leaves the bucket order */
     134           0 : #define NO_PEER   (10L*1000000L)    /* a req with no peer to pick waits this long */
     135             : 
     136             : static fd_ed25519_sig_t const sig_null = {0};
     137             : 
     138           0 : #define IN_KIND_GENESIS (0)
     139           0 : #define IN_KIND_SHRED   (1)
     140           0 : #define IN_KIND_SNAP    (2)
     141           0 : #define IN_KIND_VOTOR   (3)
     142           0 : #define IN_KIND_NET     (4)
     143           0 : #define IN_KIND_GOSSIP  (5)
     144           0 : #define IN_KIND_REPLAY  (6)
     145           0 : #define IN_KIND_SIGN    (7)
     146           0 : #define IN_KIND_EPOCH   (8)
     147             : 
     148             : struct fd_repair_tile {
     149             : 
     150             :   /* Signing */
     151             : 
     152             :   fd_pubkey_t      identity_key;
     153             :   int              staked;
     154             :   fd_keyswitch_t * keyswitch;
     155             :   int              halt_signing;
     156             :   sign_t *         sign_pool;
     157             :   ulong            sign_cnt;
     158             :   struct {
     159             :     ulong       idx;
     160             :     fd_wksp_t * mem;
     161             :     ulong       chunk0;
     162             :     ulong       wmark;
     163             :     ulong       chunk;
     164             :     ulong       free;
     165             :   } sign[SIGN_MAX];
     166             :   uchar sign_buf[FD_ED25519_SIG_SZ];
     167             : 
     168             :   /* Catchup */
     169             : 
     170             :   ulong     cert_slot0;      /* staked: first cert slot, 0 if none yet */
     171             :   ulong     turbine_slot0;   /* unstaked: first turbine slot, 0 if none yet */
     172             :   ulong     catchup_slot;    /* highest slot past the window with a verified cert (staked) or signed future shred (unstaked), 0 if none yet */
     173             :   ulong     highest_fec_complete_slot; /* highest slot a FEC set completed for off the network, our leader sets excluded: the cluster tip, 0 if none yet */
     174             : 
     175             :   /* Snapshot */
     176             : 
     177             :   ulong     snapshot_slot;   /* root of the snapshot being loaded, applied at FD_SSMSG_DONE */
     178             :   fd_mr32_t snapshot_blk_mr;
     179             : 
     180             :   /* Epoch metadata */
     181             : 
     182             :   fd_multi_epoch_leaders_t * mleaders;
     183             : 
     184             :   /* Repair data structures */
     185             : 
     186             :   ulong               seed;
     187             :   fd_repair_t *       repair;
     188             :   fd_repair_strat_t * strat;
     189             : 
     190             :   /* Requests */
     191             : 
     192             :   fd_clock_tile_t   clock[1];
     193             :   fd_rng_t          rng[1];
     194             :   fd_chacha_rng_t   nonce_rng[1];
     195             :   fd_rnonce_ss_t    rnonce_ss[1];
     196             :   timeout_t *       eager_prq;
     197             :   timeout_t *       notar_prq;
     198             :   timeout_t *       final_prq;
     199             :   pending_t *       pending_pool;
     200             :   pending_map_t *   pending_map;
     201             :   pending_dlist_t * pending_dlist;
     202             : 
     203             :   /* Networking */
     204             : 
     205             :   int               allow_private_address;
     206             :   int               report_repair_requests;
     207             :   fd_pubkey_t *     ping_deque;
     208             :   fd_ip4_udp_hdrs_t net_hdr[1];
     209             :   ushort            net_id;
     210             :   uchar             net_buf[ FD_NET_MTU ];
     211             : 
     212             :   /* Links */
     213             : 
     214             :   int in_kind[ FD_TOPO_MAX_TILE_IN_LINKS ];
     215             :   struct {
     216             :     fd_wksp_t * mem;
     217             :     ulong       chunk0;
     218             :     ulong       wmark;
     219             :     ulong       mtu;
     220             :     ulong       sign_idx; /* IN_KIND_SIGN, its sign tile in sign */
     221             :   } in[ FD_TOPO_MAX_TILE_IN_LINKS ];
     222             :   ulong chunk; /* reliable ins, after_frag */
     223             :   fd_replay_message_t replay_buf; /* replay_slot is unreliable */
     224             : 
     225             :   ulong       net_out_idx;
     226             :   fd_wksp_t * net_out_mem;
     227             :   ulong       net_out_chunk0;
     228             :   ulong       net_out_wmark;
     229             :   ulong       net_out_chunk;
     230             : 
     231             :   ulong       replay_out_idx;
     232             :   fd_wksp_t * replay_out_mem;
     233             :   ulong       replay_out_chunk0;
     234             :   ulong       replay_out_wmark;
     235             :   ulong       replay_out_chunk;
     236             : 
     237             :   ulong       rserve_out_idx;  /* ULONG_MAX if rserve is disabled */
     238             :   fd_wksp_t * rserve_out_mem;
     239             :   ulong       rserve_out_chunk0;
     240             :   ulong       rserve_out_wmark;
     241             :   ulong       rserve_out_chunk;
     242             : 
     243             :   /* Events */
     244             : 
     245             :   fd_event_block_received_t * event;
     246             : 
     247             :   /* Metrics */
     248             : 
     249             :   struct {
     250             :     ulong      pkt_tx;
     251             :     ulong      request_tx[ FD_REPAIR_SERDE_TAG_WINDOW_INDEX_FOR_BLOCK_ID+1U ]; /* by FD_REPAIR_SERDE_TAG_* */
     252             :     ulong      ping_tx;
     253             :     ulong      slot_highest_repaired;
     254             :     ulong      slot_current;
     255             :     ulong      fec_delivered;
     256             :     ulong      shred_old;
     257             :     ulong      shred_rx[ FD_METRICS_ENUM_SHRED_RESPONSE_MATCH_RESULT_CNT ];
     258             :     ulong      meta_rx;
     259             :     ulong      meta_malformed;
     260             :     ulong      meta_unsolicited;
     261             :     ulong      parent_fec_count_ok;
     262             :     ulong      parent_fec_count_failed;
     263             :     ulong      fec_root_ok;
     264             :     ulong      fec_root_failed;
     265             :     ulong      replay_root_advanced;
     266             :     ulong      replay_missing_fec;
     267             :     ulong      ping_malformed;
     268             :     ulong      ping_unknown_peer;
     269             :     ulong      ping_signature_failed;
     270             :     ulong      req_cancelled;
     271             :     ulong      req_no_peer;
     272             :     ulong      req_no_staked_peer;
     273             :     ulong      timeout_deferred[ FD_METRICS_ENUM_TIMEOUT_DEFER_REASON_CNT ];
     274             :     ulong      timeout_full;
     275             :     ulong      timeout_paused;
     276             :     ulong      sign_halted;
     277             :     ulong      ping_sign_unavailable;
     278             :     ulong      ping_address_mismatch;
     279             :     ulong      ping_unsolicited;
     280             :     ulong      req_hedged;
     281             :     ulong      req_expired;
     282             :     ulong      req_stuck_escaped;
     283             :     ulong      req_late_attempt;
     284             :     ulong      peer_byid_banned;
     285             :     ulong      byid_expired;
     286             :     fd_histf_t response_latency[ 1 ];
     287             :     fd_histf_t retry_delay[ 1 ];
     288             :     fd_histf_t escape_hedge[ 1 ];
     289             :   } metrics[ 1 ];
     290             : };
     291             : typedef struct fd_repair_tile fd_repair_tile_t;
     292             : 
     293             : /* push_timeout inserts req into its blk's role's timeout_prq.  A full
     294             :    timeout_prq drops it and rewinds the blk, back into its blk_treap,
     295             :    so the work is discovered again. */
     296             : 
     297             : static void
     298             : push_timeout( fd_repair_tile_t * ctx,
     299             :               fd_repair_blk_t *  blk,
     300           0 :               timeout_t const *  req ) {
     301           0 :   fd_repair_slot_meta_t const * meta = fd_repair_slot_meta( ctx->repair, blk->slot );
     302           0 :   ulong                         idx  = fd_repair_blk_pool_idx( ctx->repair->blk_pool, blk );
     303           0 :   timeout_t *                   prq  = meta->final_blk_idx==idx ? ctx->final_prq : meta->notar_blk_idx==idx ? ctx->notar_prq : ctx->eager_prq;
     304           0 :   if( FD_LIKELY( timeout_prq_cnt( prq )<timeout_prq_max( prq ) ) ) {
     305           0 :     timeout_prq_insert( prq, req );
     306           0 :     return;
     307           0 :   }
     308           0 :   ctx->metrics->timeout_full++;
     309           0 :   if( FD_LIKELY( req->tag==FD_REPAIR_SERDE_TAG_WINDOW_INDEX ) ) blk->wait_fec_cnt = fd_uint_min( blk->wait_fec_cnt, req->fec_idx );
     310           0 :   blk->meta_req    = ( blk->meta_req    && req->tag!=FD_REPAIR_SERDE_TAG_PARENT_AND_FEC_SET_COUNT );
     311           0 :   blk->highest_req = ( blk->highest_req && req->tag!=FD_REPAIR_SERDE_TAG_HIGHEST_WINDOW_INDEX      );
     312           0 :   blk->orphan_req  = ( blk->orphan_req  && req->tag!=FD_REPAIR_SERDE_TAG_ORPHAN                    );
     313           0 :   fd_repair_blk_treap_insert( ctx->repair, blk );
     314           0 : }
     315             : 
     316             : /* poll_timeout polls for one or more requests that have timed out. */
     317             : 
     318             : static fd_repair_blk_t *
     319             : poll_timeout( fd_repair_tile_t * ctx,
     320             :               long               now,
     321           0 :               timeout_t *        out ) {
     322           0 :   timeout_t * prqs[3] = { ctx->final_prq, ctx->notar_prq, ctx->eager_prq };
     323           0 :   for( ulong c=0UL; c<3; c++ ) {
     324           0 :     timeout_t * prq = prqs[ c ];
     325           0 :     while( timeout_prq_cnt( prq ) && prq[ 0 ].timeout<=now ) {
     326           0 :       timeout_t req = prq[ 0 ];
     327           0 :       timeout_prq_remove_min( prq );
     328             : 
     329           0 :       if( FD_UNLIKELY( req.slot <= ctx->repair->root ) ) continue;
     330           0 :       fd_repair_blk_t * blk = fd_repair_blk_map_ele_query( ctx->repair->blk_map, &req.slot, NULL, ctx->repair->blk_pool );
     331           0 :       while( blk && fd_repair_blk_pool_idx( ctx->repair->blk_pool, blk )!=req.blk ) blk = (fd_repair_blk_t *)fd_repair_blk_map_ele_next_const( blk, NULL, ctx->repair->blk_pool ); /* the req outlived its blk */
     332           0 :       if( FD_UNLIKELY( !blk ) ) { ctx->metrics->req_cancelled++; continue; }
     333             : 
     334           0 :       fd_repair_slot_meta_t const * meta = fd_repair_slot_meta( ctx->repair, blk->slot );
     335           0 :       if( FD_UNLIKELY( meta->final_blk_idx!=req.blk && meta->notar_blk_idx!=req.blk && meta->eager_blk_idx==req.blk && !blk->eager ) ) { ctx->metrics->req_cancelled++; continue; } /* the slot's eager blk, never repaired */
     336           0 :       fd_repair_fec_t const * fec = fd_repair_fec_pool_ele_const( ctx->repair->fec_pool, fd_ulong_if( req.tag==FD_REPAIR_SERDE_TAG_WINDOW_INDEX, blk->fecs[ req.fec_idx ], fd_repair_fec_pool_idx_null( ctx->repair->fec_pool ) ) );
     337           0 :       if( FD_UNLIKELY( req.tag==FD_REPAIR_SERDE_TAG_PARENT_AND_FEC_SET_COUNT && blk->parent_slot!=ULONG_MAX                   ) ) { ctx->metrics->req_cancelled++; continue; }
     338           0 :       if( FD_UNLIKELY( req.tag==FD_REPAIR_SERDE_TAG_WINDOW_INDEX             && fec && fec->fec_complete                          ) ) { ctx->metrics->req_cancelled++; continue; }
     339           0 :       if( FD_UNLIKELY( req.tag==FD_REPAIR_SERDE_TAG_WINDOW_INDEX             && !blk->eager && req.fec_idx>=blk->cmpl_fec_cnt ) ) { ctx->metrics->req_cancelled++; continue; } /* of a freed blk whose pool idx was reused */
     340           0 :       if( FD_UNLIKELY( req.tag==FD_REPAIR_SERDE_TAG_HIGHEST_WINDOW_INDEX     && blk->cmpl_fec_cnt                             ) ) { ctx->metrics->req_cancelled++; continue; }
     341           0 :       fd_repair_blk_t const * parent = fd_repair_blk_pool_ele_const( ctx->repair->blk_pool, blk->parent );
     342           0 :       if( FD_UNLIKELY( req.tag==FD_REPAIR_SERDE_TAG_ORPHAN && ( blk->parent_slot<=ctx->repair->root || ( parent && parent->parent_slot!=ULONG_MAX ) ) ) ) { blk->orphan_req = 0; ctx->metrics->req_cancelled++; continue; } /* its ancestry showed up, died with the root, or a lower blk asks */
     343           0 :       long eager_delta = fd_repair_strat_eager_ns( ctx->strat, fd_multi_epoch_leaders_get_leader_for_slot( ctx->mleaders, blk->slot ) );
     344           0 :       if( FD_UNLIKELY( req.tag==FD_REPAIR_SERDE_TAG_HIGHEST_WINDOW_INDEX     && blk->eager_ts+eager_delta>now                  ) ) { /* turbine is not quiet yet */
     345           0 :         ctx->metrics->timeout_deferred[ FD_METRICS_ENUM_TIMEOUT_DEFER_REASON_V_TURBINE_ACTIVE_IDX ]++;
     346           0 :         req.timeout = blk->eager_ts+eager_delta;
     347           0 :         push_timeout( ctx, blk, &req );
     348           0 :         continue;
     349           0 :       }
     350           0 :       *out = req;
     351           0 :       return blk;
     352           0 :     }
     353           0 :   }
     354           0 :   return NULL;
     355           0 : }
     356             : 
     357             : /* discover new requests translating repair treaps => repair requests. */
     358             : 
     359             : static void
     360             : discover( fd_repair_tile_t * ctx,
     361             :           fd_repair_blk_t *  blk,
     362           0 :           long               now ) {
     363           0 :   fd_repair_slot_meta_t const * meta = fd_repair_slot_meta( ctx->repair, blk->slot );
     364           0 :   ulong                         idx  = fd_repair_blk_pool_idx( ctx->repair->blk_pool, blk );
     365           0 :   if( FD_UNLIKELY( blk->slot<=ctx->repair->root ) ) return;
     366           0 :   if( FD_UNLIKELY( meta->final_blk_idx!=idx && meta->notar_blk_idx!=idx && meta->eager_blk_idx==idx && !blk->eager ) ) return; /* the slot's eager blk, never repaired */
     367             : 
     368           0 :   int       eager       = blk->eager;
     369           0 :   long      eager_delta = fd_repair_strat_eager_ns( ctx->strat, fd_multi_epoch_leaders_get_leader_for_slot( ctx->mleaders, blk->slot ) );
     370           0 :   timeout_t req         = { .slot = blk->slot, .blk = fd_repair_blk_pool_idx( ctx->repair->blk_pool, blk ) };
     371             : 
     372           0 :   if( FD_UNLIKELY( !eager && blk->parent_slot==ULONG_MAX && blk->meta_req ) ) return;
     373           0 :   if( FD_UNLIKELY( !eager && blk->parent_slot==ULONG_MAX ) ) {
     374           0 :     blk->meta_req = 1;
     375           0 :     req.tag       = FD_REPAIR_SERDE_TAG_PARENT_AND_FEC_SET_COUNT;
     376           0 :     req.timeout   = now;
     377           0 :     push_timeout( ctx, blk, &req );
     378           0 :     return;
     379           0 :   }
     380             : 
     381           0 :   fd_repair_blk_t const * parent      = fd_repair_blk_pool_ele_const( ctx->repair->blk_pool, blk->parent );
     382           0 :   int                     orphan_root = !parent || parent->parent_slot==ULONG_MAX; /* the root of its disconnected subtree, the blks under it wait on it */
     383           0 :   int                     orphaned    = orphan_root && !blk->orphan_req && blk->parent_slot!=ULONG_MAX && blk->parent_slot>ctx->repair->root && memcmp( &blk->parent_dmr, &hash_null, sizeof(fd_mr32_t) ) && !( eager && meta->invalidated );
     384           0 :   if( FD_UNLIKELY( orphaned ) ) {
     385           0 :     blk->orphan_req = 1;
     386           0 :     req.tag         = FD_REPAIR_SERDE_TAG_ORPHAN; /* its ancestry is unknown, learn up to 11 of it in one reply */
     387           0 :     req.timeout     = fd_long_if( eager, blk->eager_ts+eager_delta, now ); /* an eager blk's parent may still be on turbine */
     388           0 :     push_timeout( ctx, blk, &req );
     389           0 :   }
     390             : 
     391           0 :   if( FD_UNLIKELY( eager && !blk->cmpl_fec_cnt && !blk->highest_req ) ) {
     392           0 :     blk->highest_req = 1;
     393           0 :     req.tag          = FD_REPAIR_SERDE_TAG_HIGHEST_WINDOW_INDEX;
     394           0 :     req.timeout      = blk->eager_ts+eager_delta;
     395           0 :     push_timeout( ctx, blk, &req );
     396           0 :   }
     397             : 
     398           0 :   timeout_t * prq          = meta->final_blk_idx==idx ? ctx->final_prq : meta->notar_blk_idx==idx ? ctx->notar_prq : ctx->eager_prq;
     399           0 :   uint        cmpl_fec_cnt = fd_uint_if( !!blk->cmpl_fec_cnt, blk->cmpl_fec_cnt, fd_uint_if( eager, blk->rcvd_fec_cnt, 0U ) );
     400           0 :   uint        fec_idx      = blk->wait_fec_cnt;
     401           0 :   for( ; fec_idx<cmpl_fec_cnt && timeout_prq_cnt( prq )<timeout_prq_max( prq ); fec_idx++ ) {
     402           0 :     fd_repair_fec_t const * fec = fd_repair_fec_pool_ele_const( ctx->repair->fec_pool, blk->fecs[ fec_idx ] );
     403           0 :     if( FD_LIKELY( fec && fec->fec_complete ) ) continue;
     404           0 :     req.tag     = FD_REPAIR_SERDE_TAG_WINDOW_INDEX;
     405           0 :     req.fec_idx = fec_idx;
     406           0 :     req.timeout = fd_long_if( eager, ( fec ? fec->first_ts : blk->eager_ts )+eager_delta, now ); /* a partial set waits from its first shred, a missing one from the last set turbine showed */
     407           0 :     push_timeout( ctx, blk, &req );
     408           0 :   }
     409           0 :   blk->wait_fec_cnt = fec_idx;
     410           0 :   if( FD_LIKELY( timeout_prq_cnt( prq )<timeout_prq_max( prq ) ) ) return;
     411           0 :   int paused = fd_repair_blk_treap_insert( ctx->repair, blk ); /* timeout_prq filled up, resume later */
     412           0 :   ctx->metrics->timeout_paused += (ulong)paused;
     413           0 : }
     414             : 
     415             : FD_STATIC_ASSERT( FD_FEC_BLK_MAX<=FD_EVENT_BLOCK_RECEIVED_FEC_SETS_MAX, block_received_fec_sets );
     416             : 
     417             : static void
     418             : report_block_received( void *                  ctx_,
     419           0 :                        fd_repair_blk_t const * blk ) {
     420           0 :   if( FD_LIKELY( !fd_event_tl ) ) return;
     421           0 :   fd_repair_tile_t *      ctx       = (fd_repair_tile_t *)ctx_;
     422           0 :   uint                    fec_cnt   = fd_uint_if( !!blk->cmpl_fec_cnt, blk->cmpl_fec_cnt, blk->rcvd_fec_cnt ); /* an incomplete blk reports what it received */
     423           0 :   fd_repair_fec_t const * last      = fd_repair_fec_pool_ele_const( ctx->repair->fec_pool, fec_cnt ? blk->fecs[ fec_cnt-1U ] : fd_repair_fec_pool_idx_null( ctx->repair->fec_pool ) );
     424           0 :   int                     is_leader = last && last->is_leader;
     425           0 :   int                     is_known  = fd_repair_slot_meta( ctx->repair, blk->slot )->eager_blk_idx!=fd_repair_blk_pool_idx( ctx->repair->blk_pool, blk );
     426           0 :   fd_event_block_received_t * ev =  ctx->event;
     427           0 :   fd_memset( ev, 0, FD_EVENT_BLOCK_RECEIVED_PREFIX_SZ );
     428           0 :   ev->slot             = blk->slot;
     429           0 :   ev->parent_slot      = blk->parent_slot;
     430           0 :   ev->cancelled        = !!blk->telemetry.cancelled_reason;
     431           0 :   ev->cancelled_reason = fd_int_if( !!blk->telemetry.cancelled_reason, blk->telemetry.cancelled_reason, FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_NOT_CANCELLED );
     432           0 :   ev->cancelled_time   = fd_ulong_if( ev->cancelled, (ulong)blk->telemetry.cancelled_ts, 0UL );
     433           0 :   ev->notarized        = is_known && !is_leader;
     434           0 :   ev->is_leader        = is_leader;
     435           0 :   ev->caught_up        = fd_ulong_max( ctx->metrics->slot_current, ctx->catchup_slot )<=ctx->metrics->slot_highest_repaired+4UL;
     436           0 :   ev->fec_set_count    = blk->cmpl_fec_cnt;
     437           0 :   ev->slot_complete    = !!blk->cmpl_fec_cnt;
     438           0 :   ev->equivocation_detected_shred  = blk->telemetry.cancelled_reason==FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_MERKLE_ROOT_MISMATCH;
     439           0 :   ev->last_completed_fec_set_index = blk->telemetry.last_cmpl_fec_idx;
     440           0 :   ev->last_shred_received_time     = (ulong)blk->telemetry.last_shred_ts;
     441           0 :   ev->turbine_shred_received       = blk->telemetry.turbine_shred_cnt;
     442           0 :   ev->repair_shred_received        = blk->telemetry.repair_shred_cnt;
     443           0 :   ev->recovered_shred_count        = blk->telemetry.recovered_shred_cnt;
     444           0 :   if( FD_LIKELY( !is_leader ) ) { /* our own blocks are never repaired */
     445           0 :     ev->repair_request_window_count             = blk->telemetry.req_window_cnt;
     446           0 :     ev->repair_request_highest_window_count     = blk->telemetry.req_highest_cnt;
     447           0 :     ev->repair_request_orphan_count             = blk->telemetry.req_orphan_cnt;
     448           0 :     ev->repair_request_shred_for_block_id_count = blk->telemetry.req_shred_bid_cnt;
     449           0 :     ev->repair_request_parent_fec_count         = blk->telemetry.req_parent_cnt;
     450           0 :     ev->repair_request_fec_root_count           = blk->telemetry.req_fec_root_cnt;
     451           0 :     ev->first_repair_request_time               = (ulong)blk->telemetry.first_req_ts;
     452           0 :     ev->repair_shred_responses_received         = blk->telemetry.shred_res_cnt;
     453           0 :     ev->parent_fec_count_responses_received     = blk->telemetry.parent_res_cnt;
     454           0 :     ev->fec_root_responses_received             = blk->telemetry.fec_root_res_cnt;
     455           0 :     ev->last_repair_received_time               = (ulong)blk->telemetry.last_shred_res_ts;
     456           0 :     ev->first_meta_received_time                = (ulong)blk->telemetry.first_meta_res_ts;
     457           0 :   }
     458           0 :   memcpy( ev->block_id,        blk->dmr.uc,           sizeof(fd_mr32_t) );
     459           0 :   memcpy( ev->parent_block_id, blk->parent_dmr.uc, sizeof(fd_mr32_t) );
     460           0 :   ev->fec_sets_cnt = fd_ulong_min( fec_cnt, FD_EVENT_BLOCK_RECEIVED_FEC_SETS_MAX );
     461           0 :   for( ulong k=0UL; k<ev->fec_sets_cnt; k++ ) {
     462           0 :     fd_repair_fec_t const *              fec = fd_repair_fec_pool_ele_const( ctx->repair->fec_pool, blk->fecs[ k ] );
     463           0 :     fd_event_block_received_fec_sets_t * f   = &ev->fec_sets[ k ];
     464           0 :     fd_memset( f, 0, sizeof(fd_event_block_received_fec_sets_t) );
     465           0 :     if( FD_UNLIKELY( !fec ) ) continue; /* an incomplete blk can miss FEC sets */
     466           0 :     if( FD_LIKELY( fec->fec_complete ) ) memcpy( f->merkle_root, fec->mr.mr32.uc, sizeof(fd_mr32_t) ); /* only the 20-byte key is known before */
     467           0 :     f->index                      = (uint)k*FD_FEC_SHRED_CNT;
     468           0 :     f->data_shreds_received_mask   = fd_uint_if( fec->fec_complete, fec->data_rcvd, 0U );
     469           0 :     f->parity_shreds_received_mask = fec->parity_rcvd;
     470           0 :     f->repair_shreds_received_mask = fec->repair_rcvd;
     471           0 :     f->first_shred_received_time  = (ulong)fec->first_ts;
     472           0 :     f->completed_time             = (ulong)fec->cmpl_ts;
     473           0 :     f->final_shred_source         = fec->is_leader     ? FD_EVENT_BLOCK_RECEIVED_FEC_SETS_FINAL_SHRED_SOURCE_LEADER  :
     474           0 :                                     !fec->fec_complete ? 0                                                           :
     475           0 :                                     !fec->data_rcvd    ? 0                                                           :
     476           0 :                                     fec->repair_rcvd   ? FD_EVENT_BLOCK_RECEIVED_FEC_SETS_FINAL_SHRED_SOURCE_REPAIR  :
     477           0 :                                                          FD_EVENT_BLOCK_RECEIVED_FEC_SETS_FINAL_SHRED_SOURCE_TURBINE;
     478           0 :     ev->first_shred_received_time = fd_ulong_if( fec->first_ts && ( !ev->first_shred_received_time || (ulong)fec->first_ts<ev->first_shred_received_time ), (ulong)fec->first_ts, ev->first_shred_received_time );
     479           0 :   }
     480           0 :   fd_event_report_block_received( ev );
     481           0 : }
     482             : 
     483             : static void
     484             : report_repair_request( fd_repair_tile_t * ctx,
     485             :                        pending_t const *  pending,
     486             :                        uint               bit_idx,
     487             :                        long               res_ts,
     488             :                        int                outcome,
     489           0 :                        int                ban ) {
     490           0 :   if( FD_LIKELY( !ctx->report_repair_requests || !fd_event_tl ) ) return;
     491           0 :   fd_repair_strat_peer_t const * peer = fd_repair_strat_query( ctx->strat, &pending->peer );
     492           0 :   fd_event_repair_request_t      ev   = {0};
     493           0 :   memcpy( ev.peer_identity, pending->peer.uc, sizeof(fd_pubkey_t) );
     494           0 :   if( FD_LIKELY( pending->ip4 ) ) {
     495           0 :     FD_STORE( ushort, ev.peer_ip+10, (ushort)0xffff );
     496           0 :     FD_STORE( uint,   ev.peer_ip+12, pending->ip4 );
     497           0 :   }
     498           0 :   switch( pending->tag ) {
     499           0 :   case FD_REPAIR_SERDE_TAG_WINDOW_INDEX:              ev.type = FD_EVENT_REPAIR_REQUEST_TYPE_WINDOW_INDEX;              break;
     500           0 :   case FD_REPAIR_SERDE_TAG_HIGHEST_WINDOW_INDEX:      ev.type = FD_EVENT_REPAIR_REQUEST_TYPE_HIGHEST_WINDOW_INDEX;      break;
     501           0 :   case FD_REPAIR_SERDE_TAG_ORPHAN:                    ev.type = FD_EVENT_REPAIR_REQUEST_TYPE_ORPHAN;                    break;
     502           0 :   case FD_REPAIR_SERDE_TAG_PARENT_AND_FEC_SET_COUNT:  ev.type = FD_EVENT_REPAIR_REQUEST_TYPE_PARENT_AND_FEC_SET_COUNT;  break;
     503           0 :   case FD_REPAIR_SERDE_TAG_FEC_SET_ROOT:              ev.type = FD_EVENT_REPAIR_REQUEST_TYPE_FEC_SET_ROOT;              break;
     504           0 :   case FD_REPAIR_SERDE_TAG_WINDOW_INDEX_FOR_BLOCK_ID: ev.type = FD_EVENT_REPAIR_REQUEST_TYPE_WINDOW_INDEX_FOR_BLOCK_ID; break;
     505           0 :   default: FD_LOG_CRIT(( "unhandled request tag %u", pending->tag ));
     506           0 :   }
     507           0 :   int fec = pending->tag==FD_REPAIR_SERDE_TAG_WINDOW_INDEX || pending->tag==FD_REPAIR_SERDE_TAG_WINDOW_INDEX_FOR_BLOCK_ID || pending->tag==FD_REPAIR_SERDE_TAG_FEC_SET_ROOT || pending->tag==FD_REPAIR_SERDE_TAG_HIGHEST_WINDOW_INDEX;
     508           0 :   memcpy( ev.block_id, pending->dmr.uc, sizeof(fd_mr32_t) );
     509           0 :   ev.peer_port     = pending->port;
     510           0 :   ev.peer_rank     = peer && (ulong)( peer-ctx->strat->cur.peers )<ctx->strat->staked_cnt ? (ushort)( peer-ctx->strat->cur.peers ) : USHORT_MAX;
     511           0 :   ev.slot          = pending->slot;
     512           0 :   ev.shred_idx     = fd_uint_if( fec, pending->fec_idx*FD_FEC_SHRED_CNT+bit_idx, 0U );
     513           0 :   ev.req_time      = (ulong)pending->ts;
     514           0 :   ev.res_time      = (ulong)res_ts;
     515           0 :   ev.outcome       = outcome;
     516           0 :   ev.ban           = ban;
     517           0 :   ev.attempt       = pending->attempt;
     518           0 :   ev.pick          = pending->pick;
     519           0 :   ev.role          = pending->role;
     520           0 :   fd_event_report_repair_request( &ev );
     521           0 : }
     522             : 
     523             : 
     524             : static inline void
     525             : handle_shred( fd_repair_tile_t * ctx,
     526             :               ulong              sig,
     527             :               uchar const *      chunk,
     528           0 :               long               rx_ts ) {
     529           0 :   if( FD_UNLIKELY( fd_shred_sig_src( sig )==SHRED_SIG_FEC_EVICTED ) ) {
     530           0 :     fd_fec_evicted_t const * evicted = (fd_fec_evicted_t const *)fd_type_pun_const( chunk );
     531           0 :     if( FD_UNLIKELY( evicted->slot<=ctx->repair->root || evicted->slot>=ctx->repair->root+ctx->repair->slot_max ) ) return; /* outside the window */
     532           0 :     if( FD_UNLIKELY( evicted->fec_set_idx>=ctx->repair->fec_blk_max*FD_FEC_SHRED_CNT ) ) return;
     533           0 :     fd_mr_t key; memcpy( key.mr32.uc, evicted->merkle_root.uc, sizeof(fd_mr32_t) );
     534           0 :     fd_repair_fec_evicted( ctx->repair, evicted->slot, evicted->fec_set_idx, &key );
     535           0 :     return;
     536           0 :   }
     537             : 
     538           0 :   ulong slot = ((fd_shred_base_t const *)fd_type_pun_const( chunk ))->shred.slot; /* complete and shred messages share the header position */
     539           0 :   ctx->metrics->shred_old          += (ulong)( slot<=ctx->repair->root && fd_shred_sig_src( sig )<=SHRED_SIG_SRC_BAD_REPAIR );
     540           0 :   ctx->metrics->slot_current        = fd_ulong_if( slot>ctx->repair->root && fd_shred_sig_src( sig )<=SHRED_SIG_SRC_BAD_REPAIR, fd_ulong_max( ctx->metrics->slot_current, slot ), ctx->metrics->slot_current );
     541           0 :   int turbine_first = slot>ctx->repair->root && slot<ctx->repair->root+ctx->repair->slot_max && fd_shred_sig_src( sig )==SHRED_SIG_SRC_TURBINE && !ctx->turbine_slot0;
     542           0 :   ctx->turbine_slot0 = fd_ulong_if( turbine_first, slot, ctx->turbine_slot0 );
     543           0 :   if( FD_UNLIKELY( slot<=ctx->repair->root || slot>=ctx->repair->root+ctx->repair->slot_max ) ) return; /* outside the window */
     544             : 
     545           0 :   if( FD_UNLIKELY( turbine_first && !ctx->staked ) ) fd_repair_slot_catchup( ctx->repair, slot ); /* unstaked has no certs, turbine is untrusted so staked never does this */
     546             : 
     547           0 :   switch( fd_shred_sig_src( sig ) ) {
     548           0 :   case SHRED_SIG_FEC_COMPLETE:
     549           0 :   case SHRED_SIG_FEC_COMPLETE_LEADER:
     550           0 :   case SHRED_SIG_FEC_COMPLETE_AGAIN: {
     551           0 :     fd_fec_complete_t const * complete = (fd_fec_complete_t const *)fd_type_pun_const( chunk );
     552           0 :     fd_shred_t const *        shred    = &complete->last_shred_hdr;
     553             : 
     554           0 :     ctx->highest_fec_complete_slot = fd_ulong_max( ctx->highest_fec_complete_slot, shred->slot );
     555           0 :     if( FD_UNLIKELY( shred->idx>=ctx->repair->fec_blk_max*FD_FEC_SHRED_CNT                           ) ) return;
     556           0 :     if( FD_UNLIKELY( !shred->data.parent_off || shred->data.parent_off>shred->slot-ctx->repair->root ) ) return; /* parent is the slot itself, or below the root */
     557             : 
     558           0 :     fd_repair_fec_t const * fec = fd_repair_fec_complete( ctx->repair, shred, fd_type_pun_const( &complete->merkle_root ), sig==SHRED_SIG_FEC_COMPLETE_LEADER, (uint)complete->turbine_shred_cnt, (uint)complete->repair_shred_cnt, (uint)complete->reconstructed_shred_cnt, complete->data_shred_rcvd, complete->parity_shred_rcvd, complete->repair_data_rcvd, rx_ts );
     559           0 :     if( FD_UNLIKELY( sig!=SHRED_SIG_FEC_COMPLETE || !fec || !fec->first_ts ) ) break; /* a first completion from the network */
     560             : 
     561           0 :     fd_repair_strat_turbine_done( ctx->strat, fd_multi_epoch_leaders_get_leader_for_slot( ctx->mleaders, shred->slot ), rx_ts-fec->first_ts );
     562           0 :     break;
     563           0 :   }
     564           0 :   case SHRED_SIG_SRC_TURBINE:
     565           0 :   case SHRED_SIG_SRC_LEADER:
     566           0 :   case SHRED_SIG_SRC_RECONSTRUCTED:
     567           0 :   case SHRED_SIG_SRC_REPAIR:
     568           0 :   case SHRED_SIG_SRC_BAD_REPAIR: {
     569           0 :     fd_shred_base_t const * msg   = (fd_shred_base_t const *)fd_type_pun_const( chunk );
     570           0 :     fd_shred_t const *      shred = &msg->shred;
     571           0 :     long                    now   = fd_clock_tile_now( ctx->clock );
     572             : 
     573           0 :     if( FD_UNLIKELY( fd_shred_sig_res( sig )==SHRED_SIG_RESULT_EQVOC ) ) {
     574           0 :       ctx->metrics->shred_rx[ FD_METRICS_ENUM_SHRED_RESPONSE_MATCH_RESULT_V_EQVOC_IDX ] += (ulong)( fd_shred_sig_src( sig )==SHRED_SIG_SRC_REPAIR || fd_shred_sig_src( sig )==SHRED_SIG_SRC_BAD_REPAIR );
     575           0 :       fd_repair_slot_invalidated( ctx->repair, shred->slot, FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_MERKLE_ROOT_MISMATCH, now );
     576           0 :       return;
     577           0 :     }
     578             : 
     579           0 :     if( FD_UNLIKELY( !fd_shred_is_data( fd_shred_type( shred->variant ) ) ) ) return;
     580           0 :     if( FD_UNLIKELY( shred->idx>=ctx->repair->fec_blk_max*FD_FEC_SHRED_CNT ) ) return;
     581             : 
     582             :     /* A repair shred answers the request whose nonce it carries: the
     583             :        first one ends that request's pick, the last one ends the
     584             :        request.  A HighestWindowIndex answer that does not complete the
     585             :        slot arrives as BAD_REPAIR. */
     586             : 
     587           0 :     int         normal  = fd_rnonce_ss_normal_repair( msg->rnonce );
     588           0 :     int         reply   = fd_shred_sig_src( sig )==SHRED_SIG_SRC_REPAIR || fd_shred_sig_src( sig )==SHRED_SIG_SRC_BAD_REPAIR;
     589           0 :     ulong       key     = (ulong)(uint)slot<<32 | fd_uint_if( normal, shred->idx/FD_FEC_SHRED_CNT, (uint)ctx->repair->fec_blk_max );
     590           0 :     uint        bit     = fd_uint_if( normal, 1U<<( shred->idx%FD_FEC_SHRED_CNT ), 1U );
     591           0 :     pending_t * pending = reply ? pending_map_ele_query( ctx->pending_map, &key, NULL, ctx->pending_pool ) : NULL;
     592           0 :     for( ; pending; pending = (pending_t *)pending_map_ele_next_const( pending, NULL, ctx->pending_pool ) ) {
     593           0 :       if( FD_UNLIKELY( !( pending->mask & bit ) ) ) continue; /* already answered, or not asked of this request */
     594           0 :       if( FD_LIKELY( fd_rnonce_ss_compute( ctx->rnonce_ss, normal, slot, shred->idx, pending->ts )!=msg->rnonce ) ) continue;
     595           0 :       if( FD_LIKELY( !pending->answered ) ) { /* rtt to the shred tile's network rx, not to this tile's turn at the frag */
     596           0 :         long rtt = fd_long_max( rx_ts-pending->ts, 1L );
     597           0 :         fd_repair_strat_request_done( ctx->strat, &pending->peer, rtt, pending->tag==FD_REPAIR_SERDE_TAG_WINDOW_INDEX_FOR_BLOCK_ID );
     598           0 :         fd_histf_sample( ctx->metrics->response_latency, (ulong)rtt );
     599           0 :       }
     600           0 :       pending->mask &= ~bit;
     601           0 :       pending->answered++;
     602           0 :       report_repair_request( ctx, pending, (uint)fd_uint_find_lsb( bit ), rx_ts, FD_EVENT_REPAIR_REQUEST_OUTCOME_ANSWERED, FD_EVENT_REPAIR_REQUEST_BAN_NONE );
     603           0 :       ctx->metrics->shred_rx[ fd_ulong_if( pending->tag==FD_REPAIR_SERDE_TAG_WINDOW_INDEX_FOR_BLOCK_ID, FD_METRICS_ENUM_SHRED_RESPONSE_MATCH_RESULT_V_FOR_BLOCK_ID_IDX, FD_METRICS_ENUM_SHRED_RESPONSE_MATCH_RESULT_V_SLOT_INDEX_IDX ) ]++;
     604           0 :       fd_repair_blk_t * blk = fd_repair_blk_map_ele_query( ctx->repair->blk_map, &pending->slot, NULL, ctx->repair->blk_pool );
     605           0 :       while( blk && memcmp( &blk->dmr, &pending->dmr, sizeof(fd_mr32_t) ) ) blk = (fd_repair_blk_t *)fd_repair_blk_map_ele_next_const( blk, NULL, ctx->repair->blk_pool ); /* not by pending->blk, it may be freed or reused, and a notar blk lives on as eager after dedup */
     606           0 :       if( FD_LIKELY( blk && ( pending->tag==FD_REPAIR_SERDE_TAG_WINDOW_INDEX || pending->tag==FD_REPAIR_SERDE_TAG_WINDOW_INDEX_FOR_BLOCK_ID ) ) ) {
     607           0 :         blk->telemetry.shred_res_cnt++;
     608           0 :         blk->telemetry.last_shred_res_ts = rx_ts;
     609           0 :       }
     610           0 :       if( FD_LIKELY( !pending->mask ) ) { /* every shred answered */
     611           0 :         pending_map_ele_remove_fast( ctx->pending_map,   pending, ctx->pending_pool );
     612           0 :         pending_dlist_ele_remove   ( ctx->pending_dlist, pending, ctx->pending_pool );
     613           0 :         pending_pool_ele_release   ( ctx->pending_pool,  pending );
     614           0 :       }
     615           0 :       break;
     616           0 :     }
     617           0 :     ctx->metrics->shred_rx[ FD_METRICS_ENUM_SHRED_RESPONSE_MATCH_RESULT_V_UNMATCHED_IDX ] += (ulong)( reply && !pending );
     618             : 
     619           0 :     if( FD_UNLIKELY( !shred->data.parent_off || shred->data.parent_off>shred->slot-ctx->repair->root ) ) return; /* parent is the slot itself, or below the root */
     620             : 
     621           0 :     fd_repair_shred_insert( ctx->repair, shred, fd_type_pun_const( &msg->merkle_root ), rx_ts );
     622           0 :     break;
     623           0 :   }
     624           0 :   default: FD_LOG_ERR(( "unhandled shred sig src %u", fd_shred_sig_src( sig ) ));
     625           0 :   }
     626           0 : }
     627             : 
     628             : static void
     629             : handle_snapshot( fd_repair_tile_t * ctx,
     630             :                  ulong              sig,
     631           0 :                  uchar const *      chunk ) {
     632           0 :   switch( fd_ssmsg_sig_message( sig ) ) {
     633           0 :   case FD_SSMSG_MANIFEST_FULL:
     634           0 :   case FD_SSMSG_MANIFEST_INCREMENTAL: {
     635           0 :     fd_snapshot_manifest_t const * manifest = (fd_snapshot_manifest_t const *)fd_type_pun_const( chunk );
     636           0 :     ctx->snapshot_slot = manifest->slot;
     637           0 :     memcpy( ctx->snapshot_blk_mr.uc, manifest->block_id, sizeof(fd_mr32_t) );
     638           0 :     break;
     639           0 :   }
     640           0 :   case FD_SSMSG_DONE:
     641           0 :     fd_repair_init( ctx->repair, ctx->snapshot_slot, &ctx->snapshot_blk_mr, report_block_received, ctx );
     642           0 :     break;
     643           0 :   default:
     644           0 :     FD_LOG_ERR(( "unexpected snapshot message %lu", fd_ssmsg_sig_message( sig ) ));
     645           0 :   }
     646           0 : }
     647             : 
     648             : static inline void
     649             : handle_votor( fd_repair_tile_t * ctx,
     650             :               ulong              sig,
     651           0 :               uchar const *      chunk ) {
     652           0 :   fd_votor_msg_t const * msg = (fd_votor_msg_t const *)fd_type_pun_const( chunk );
     653           0 :   long                   now = fd_clock_tile_now( ctx->clock );
     654             : 
     655           0 :   switch( sig ) {
     656           0 :   case FD_VOTOR_SIG_CERTED: {
     657           0 :     fd_votor_certed_t const * certed = &msg->certed;
     658           0 :     if( FD_UNLIKELY( certed->slot<=ctx->repair->root || certed->slot>=ctx->repair->root+ctx->repair->slot_max ) ) return; /* outside the window */
     659           0 :     if( FD_UNLIKELY( ctx->staked && !ctx->cert_slot0 ) ) {
     660           0 :       ctx->cert_slot0 = certed->slot;
     661           0 :       fd_repair_slot_catchup( ctx->repair, certed->slot );
     662           0 :     }
     663           0 :     switch( certed->kind ) {
     664           0 :     case FD_CERT_KIND_FINAL:
     665           0 :     case FD_CERT_KIND_FAST_FINAL:     fd_repair_blk_finalized( ctx->repair, certed->slot, fd_type_pun_const( &certed->block_id ), now ); break;
     666           0 :     case FD_CERT_KIND_NOTAR:          fd_repair_blk_notarized( ctx->repair, certed->slot, fd_type_pun_const( &certed->block_id ) ); break;
     667           0 :     case FD_CERT_KIND_NOTAR_FALLBACK: break;
     668           0 :     case FD_CERT_KIND_SKIP:           break; /* a skip cert alone is not final, see fd_repair_slot_skipped */
     669           0 :     default: FD_LOG_ERR(( "unhandled cert kind %u", certed->kind ));
     670           0 :     }
     671           0 :     break;
     672           0 :   }
     673           0 :   case FD_VOTOR_SIG_REPAIR: {
     674           0 :     ulong slot = msg->repair.slot;
     675           0 :     if( FD_UNLIKELY( slot<=ctx->repair->root ) ) return;
     676           0 :     if( FD_UNLIKELY( slot>=ctx->repair->root+ctx->repair->slot_max ) ) {
     677           0 :       if( FD_UNLIKELY( !ctx->staked || slot<=ctx->catchup_slot ) ) return;
     678           0 :       ctx->catchup_slot = slot;
     679           0 :       fd_repair_slot_catchup( ctx->repair, ctx->repair->root+ctx->repair->slot_max );
     680           0 :       return;
     681           0 :     }
     682           0 :     fd_repair_blk_notarized( ctx->repair, slot, fd_type_pun_const( &msg->repair.block_id ) );
     683           0 :     break;
     684           0 :   }
     685           0 :   case FD_VOTOR_SIG_QUORUM: {
     686           0 :     ulong slot = msg->quorum.slot;
     687           0 :     if( FD_UNLIKELY( slot<=ctx->repair->root || slot>=ctx->repair->root+ctx->repair->slot_max ) ) return; /* outside the window */
     688           0 :     switch( msg->quorum.kind ) {
     689           0 :     case FD_VOTOR_QUORUM_KIND_SAFE_TO_NOTAR:        break; /* replay already has the block */
     690           0 :     case FD_VOTOR_QUORUM_KIND_IMPLICITLY_FINALIZED: fd_repair_blk_finalized( ctx->repair, slot, fd_type_pun_const( &msg->quorum.block_id ), now ); break;
     691           0 :     case FD_VOTOR_QUORUM_KIND_IMPLICITLY_SKIPPED:   fd_repair_slot_skipped ( ctx->repair, slot );                        break;
     692           0 :     default: FD_LOG_ERR(( "unhandled quorum kind %u", msg->quorum.kind ));
     693           0 :     }
     694           0 :     break;
     695           0 :   }
     696           0 :   default: FD_LOG_ERR(( "unhandled votor sig %lu", sig ));
     697           0 :   }
     698           0 : }
     699             : 
     700             : static inline void
     701             : handle_replay( fd_repair_tile_t * ctx,
     702             :                ulong              sig,
     703           0 :                uchar const *      chunk ) {
     704           0 :   switch( sig ) {
     705           0 :   case REPLAY_SIG_ROOT_ADVANCED: {
     706           0 :     fd_replay_root_advanced_t const * root = (fd_replay_root_advanced_t const *)fd_type_pun_const( chunk );
     707           0 :     ctx->metrics->replay_root_advanced++;
     708           0 :     if( FD_UNLIKELY( root->slot<=ctx->repair->root ) ) return;
     709           0 :     fd_repair_root_advanced( ctx->repair, root->slot, fd_type_pun_const( &root->block_id ) );
     710           0 :     if( FD_UNLIKELY( ctx->catchup_slot>ctx->repair->root ) ) fd_repair_slot_catchup( ctx->repair, fd_ulong_min( ctx->catchup_slot+1UL, ctx->repair->root+ctx->repair->slot_max ) );
     711           0 :     break;
     712           0 :   }
     713           0 :   case REPLAY_SIG_SLOT_DEAD: {
     714           0 :     fd_replay_slot_dead_t const * dead = (fd_replay_slot_dead_t const *)fd_type_pun_const( chunk );
     715           0 :     if( FD_UNLIKELY( dead->slot<=ctx->repair->root ) ) return;
     716           0 :     fd_repair_blk_dead( ctx->repair, dead->slot, fd_type_pun_const( &dead->block_id ), dead->latest_fec_idx );
     717           0 :     break;
     718           0 :   }
     719           0 :   case REPLAY_SIG_MISSING_FEC:
     720           0 :     ctx->metrics->replay_missing_fec++;
     721           0 :     ctx->repair->reconsume = 1;
     722           0 :     break;
     723           0 :   default: FD_LOG_ERR(( "unhandled replay sig %lu", sig ));
     724           0 :   }
     725           0 : }
     726             : 
     727             : static void
     728             : handle_gossip( fd_repair_tile_t *                 ctx,
     729             :                ulong                              sig,
     730           0 :                fd_gossip_update_message_t const * msg ) {
     731             : 
     732           0 :   fd_pubkey_t id_key;
     733           0 :   memcpy( id_key.uc, msg->origin, sizeof(fd_pubkey_t) );
     734             : 
     735           0 :   switch( sig ) {
     736           0 :   case FD_GOSSIP_UPDATE_TAG_CONTACT_INFO: {
     737           0 :     fd_gossip_socket_t const * socket = &msg->contact_info->value->sockets[FD_GOSSIP_CONTACT_INFO_SOCKET_SERVE_REPAIR];
     738           0 :     uint                       ip4    = fd_uint_if( !socket->is_ipv6, socket->ip4, 0U );
     739           0 :     ushort                     port   = fd_ushort_bswap( socket->port );
     740           0 :     if( FD_UNLIKELY( fd_pubkey_eq( &id_key, &ctx->identity_key ) || !ip4 || !port || fd_ip4_addr_is_mcast( ip4 ) || ( !ctx->allow_private_address && !fd_ip4_addr_is_public( ip4 ) ) ) ) {
     741           0 :       fd_repair_strat_contact_info_removed( ctx->strat, &id_key );
     742           0 :       return;
     743           0 :     }
     744             : 
     745           0 :     fd_repair_strat_contact_info_updated( ctx->strat, &id_key, ip4, port );
     746           0 :     fd_repair_strat_peer_t const * peer = fd_repair_strat_query( ctx->strat, &id_key );
     747           0 :     if( FD_UNLIKELY( peer && !peer->ping_ts && !ping_deque_full( ctx->ping_deque ) ) ) ping_deque_push_tail( ctx->ping_deque, id_key ); /* new address */
     748           0 :     break;
     749           0 :   }
     750           0 :   case FD_GOSSIP_UPDATE_TAG_CONTACT_INFO_REMOVE:
     751           0 :     fd_repair_strat_contact_info_removed( ctx->strat, &id_key );
     752           0 :     break;
     753           0 :   default:
     754           0 :     FD_LOG_ERR(( "unexpected gossip sig %lu", sig ));
     755           0 :   }
     756           0 : }
     757             : 
     758             : /* Only answer a ping if it comes from the address of a peer we sent a
     759             :    request to in the last PING_TTL. */
     760             : 
     761             : static void
     762             : handle_ping( fd_repair_tile_t *   ctx,
     763             :              fd_stem_context_t *  stem,
     764             :              uchar const *        data,
     765             :              ulong                data_sz,
     766             :              fd_ip4_hdr_t const * ip4,
     767             :              fd_udp_hdr_t const * udp,
     768           0 :              long                 now ) {
     769           0 :   fd_pubkey_t      from;
     770           0 :   fd_hash_t        token;
     771           0 :   fd_ed25519_sig_t sig;
     772           0 :   if( FD_UNLIKELY( !fd_repair_ping_de( data, data_sz, &from, &token, sig ) && !fd_repair_block_id_ping_de( data, data_sz, &from, &token, sig ) ) ) { ctx->metrics->ping_malformed++; return; }
     773             : 
     774           0 :   fd_repair_strat_peer_t * peer = fd_repair_strat_query( ctx->strat, &from );
     775           0 :   if( FD_UNLIKELY( !peer                                                                  ) ) { ctx->metrics->ping_unknown_peer++; return; }
     776           0 :   if( FD_UNLIKELY( peer->ip4!=ip4->saddr || fd_ushort_bswap( peer->port )!=udp->net_sport ) ) { ctx->metrics->ping_address_mismatch++; return; }
     777           0 :   if( FD_UNLIKELY( now-peer->ping_ts>=PING_TTL                                            ) ) { ctx->metrics->ping_unsolicited++; return; }
     778           0 :   if( FD_UNLIKELY( ctx->halt_signing || !sign_pool_free( ctx->sign_pool )                 ) ) { ctx->metrics->ping_sign_unavailable++; return; }
     779             : 
     780           0 :   peer->ping_ts = now-PING_TTL; /* one verify per request we sent */
     781           0 :   fd_sha512_t sha[1];
     782           0 :   if( FD_UNLIKELY( fd_ed25519_verify( token.uc, sizeof(fd_hash_t), sig, from.uc, sha )!=FD_ED25519_SUCCESS ) ) { ctx->metrics->ping_signature_failed++; return; }
     783             : 
     784           0 :   ulong s = 0UL;
     785           0 :   for( ulong i=1UL; i<ctx->sign_cnt; i++ ) s = fd_ulong_if( ctx->sign[ i ].free>ctx->sign[ s ].free, i, s );
     786           0 :   uchar * preimage = fd_chunk_to_laddr( ctx->sign[ s ].mem, ctx->sign[ s ].chunk );
     787           0 :   memcpy( preimage,      "SOLANA_PING_PONG", 16UL              );
     788           0 :   memcpy( preimage+16UL, token.uc,           sizeof(fd_hash_t) );
     789           0 :   fd_repair_pong_t pong;
     790           0 :   fd_sha256_hash( preimage, 16UL+sizeof(fd_hash_t), pong.hash.uc );
     791             : 
     792           0 :   sign_t * pending = sign_pool_ele_acquire( ctx->sign_pool );
     793           0 :   *pending    = (sign_t){ .sig_off = FD_REPAIR_PONG_SER_SZ-FD_ED25519_SIG_SZ, .saddr = ip4->daddr, .daddr = ip4->saddr, .dport = udp->net_sport };
     794           0 :   pending->sz = fd_repair_pong_ser( &pong, sig_null, &ctx->identity_key, pending->buf );
     795           0 :   fd_stem_publish( stem, ctx->sign[ s ].idx, (sign_pool_idx( ctx->sign_pool, pending )<<32)|(ulong)FD_KEYGUARD_SIGN_TYPE_SHA256_ED25519, ctx->sign[ s ].chunk, 16UL+sizeof(fd_hash_t), 0UL, 0UL, 0UL );
     796           0 :   ctx->sign[ s ].chunk = fd_dcache_compact_next( ctx->sign[ s ].chunk, 16UL+sizeof(fd_hash_t), ctx->sign[ s ].chunk0, ctx->sign[ s ].wmark );
     797           0 :   ctx->sign[ s ].free--;
     798           0 :   ctx->metrics->request_tx[ FD_REPAIR_SERDE_TAG_PONG ]++;
     799           0 : }
     800             : 
     801             : static void
     802             : handle_parent_and_fec_set_count_res( fd_repair_tile_t *   ctx,
     803             :                                      uchar const *        data,
     804             :                                      ulong                data_sz,
     805             :                                      fd_ip4_hdr_t const * ip4,
     806             :                                      fd_udp_hdr_t const * udp,
     807             :                                      long                 now,
     808           0 :                                      long                 rx_ts ) {
     809           0 :   uint      fec_set_cnt;
     810           0 :   ulong     parent_slot;
     811           0 :   fd_hash_t parent_dmr;
     812           0 :   uchar     proof[ FD_REPAIR_PROOF_MAX ];
     813           0 :   ulong     proof_sz;
     814           0 :   uint      nonce;
     815           0 :   ctx->metrics->meta_rx++;
     816           0 :   if( FD_UNLIKELY( fd_repair_res_parent_fec_set_count_de( data, data_sz, &fec_set_cnt, &parent_slot, &parent_dmr, proof, &proof_sz, &nonce )!=data_sz ) ) { ctx->metrics->meta_malformed++; return; }
     817             : 
     818           0 :   pending_t * pending = pending_map_ele_query( ctx->pending_map, &(ulong){ nonce }, NULL, ctx->pending_pool );
     819           0 :   if( FD_UNLIKELY( !pending || pending->tag!=FD_REPAIR_SERDE_TAG_PARENT_AND_FEC_SET_COUNT ) ) { ctx->metrics->meta_unsolicited++; return; }
     820             : 
     821           0 :   fd_repair_strat_peer_t const * peer  = fd_repair_strat_query( ctx->strat, &pending->peer );
     822           0 :   int                            valid = !fd_repair_res_parent_fec_set_count_verify( fec_set_cnt, parent_slot, &parent_dmr, proof, proof_sz, &pending->dmr );
     823           0 :   int                            from  = peer && peer->ip4==ip4->saddr && fd_ushort_bswap( peer->port )==udp->net_sport;
     824           0 :   ctx->metrics->parent_fec_count_failed += (ulong)!valid;
     825           0 :   if( FD_UNLIKELY( !valid && !from ) ) return;
     826             : 
     827           0 :   pending_t sent = *pending;
     828           0 :   pending_map_ele_remove_fast( ctx->pending_map,   pending, ctx->pending_pool );
     829           0 :   pending_dlist_ele_remove   ( ctx->pending_dlist, pending, ctx->pending_pool );
     830           0 :   pending_pool_ele_release   ( ctx->pending_pool,  pending );
     831           0 :   report_repair_request( ctx, &sent, 0U, rx_ts, fd_int_if( valid, FD_EVENT_REPAIR_REQUEST_OUTCOME_ANSWERED, FD_EVENT_REPAIR_REQUEST_OUTCOME_BAD ), fd_int_if( valid, FD_EVENT_REPAIR_REQUEST_BAN_NONE, FD_EVENT_REPAIR_REQUEST_BAN_BAD_PROOF ) );
     832           0 :   if( FD_UNLIKELY( !valid ) ) {
     833           0 :     fd_repair_strat_request_failed( ctx->strat, &sent.peer, now+BAN_TTL );
     834           0 :     return;
     835           0 :   }
     836           0 :   fd_repair_strat_request_done( ctx->strat, &sent.peer, now-sent.ts, 0 );
     837           0 :   fd_histf_sample( ctx->metrics->response_latency, (ulong)( now-sent.ts ) );
     838           0 :   ctx->metrics->parent_fec_count_ok++;
     839             : 
     840           0 :   if( FD_UNLIKELY( sent.slot<=ctx->repair->root  ) ) return; /* rooted while in flight */
     841           0 :   if( FD_UNLIKELY( parent_slot<ctx->repair->root ) ) return; /* on a fork below the root */
     842           0 :   if( FD_UNLIKELY( parent_slot>=sent.slot        ) ) return; /* the leader signed a bad parent */
     843             : 
     844           0 :   fd_repair_blk_parented( ctx->repair, sent.slot, fd_type_pun_const( &sent.dmr ), parent_slot, fd_type_pun_const( &parent_dmr ), fec_set_cnt, now );
     845           0 : }
     846             : 
     847             : static void
     848             : handle_fec_set_root_res( fd_repair_tile_t *   ctx,
     849             :                          uchar const *        data,
     850             :                          ulong                data_sz,
     851             :                          fd_ip4_hdr_t const * ip4,
     852             :                          fd_udp_hdr_t const * udp,
     853             :                          long                 now,
     854           0 :                          long                 rx_ts ) {
     855           0 :   fd_mr_t   fec_mr = {0};
     856           0 :   uchar     proof[ FD_REPAIR_PROOF_MAX ];
     857           0 :   ulong     proof_sz;
     858           0 :   uint      nonce;
     859           0 :   ctx->metrics->meta_rx++;
     860           0 :   if( FD_UNLIKELY( fd_repair_res_fec_set_root_de( data, data_sz, fec_mr.mr20.uc, proof, &proof_sz, &nonce )!=data_sz ) ) { ctx->metrics->meta_malformed++; return; }
     861             : 
     862           0 :   pending_t * pending = pending_map_ele_query( ctx->pending_map, &(ulong){ nonce }, NULL, ctx->pending_pool );
     863           0 :   if( FD_UNLIKELY( !pending || pending->tag!=FD_REPAIR_SERDE_TAG_FEC_SET_ROOT ) ) { ctx->metrics->meta_unsolicited++; return; }
     864             : 
     865           0 :   fd_repair_strat_peer_t const * peer  = fd_repair_strat_query( ctx->strat, &pending->peer );
     866           0 :   int                            valid = !fd_repair_res_fec_set_root_verify( fec_mr.mr20.uc, proof, proof_sz, &pending->dmr, pending->fec_idx*FD_FEC_SHRED_CNT, pending->fec_set_cnt );
     867           0 :   int                            from  = peer && peer->ip4==ip4->saddr && fd_ushort_bswap( peer->port )==udp->net_sport;
     868           0 :   ctx->metrics->fec_root_failed  += (ulong)!valid;
     869           0 :   if( FD_UNLIKELY( !valid && !from ) ) return;
     870             : 
     871           0 :   pending_t sent = *pending;
     872           0 :   pending_map_ele_remove_fast( ctx->pending_map,   pending, ctx->pending_pool );
     873           0 :   pending_dlist_ele_remove   ( ctx->pending_dlist, pending, ctx->pending_pool );
     874           0 :   pending_pool_ele_release   ( ctx->pending_pool,  pending );
     875           0 :   report_repair_request( ctx, &sent, 0U, rx_ts, fd_int_if( valid, FD_EVENT_REPAIR_REQUEST_OUTCOME_ANSWERED, FD_EVENT_REPAIR_REQUEST_OUTCOME_BAD ), fd_int_if( valid, FD_EVENT_REPAIR_REQUEST_BAN_NONE, FD_EVENT_REPAIR_REQUEST_BAN_BAD_PROOF ) );
     876           0 :   if( FD_UNLIKELY( !valid ) ) {
     877           0 :     fd_repair_strat_request_failed( ctx->strat, &sent.peer, now+BAN_TTL );
     878           0 :     return;
     879           0 :   }
     880           0 :   fd_repair_strat_request_done( ctx->strat, &sent.peer, now-sent.ts, 0 );
     881           0 :   fd_histf_sample( ctx->metrics->response_latency, (ulong)( now-sent.ts ) );
     882           0 :   ctx->metrics->fec_root_ok++;
     883             : 
     884           0 :   if( FD_UNLIKELY( sent.slot<=ctx->repair->root ) ) return; /* rooted while in flight */
     885           0 :   fd_repair_fec_notarized( ctx->repair, sent.slot, fd_type_pun_const( &sent.dmr ), sent.fec_idx*FD_FEC_SHRED_CNT, &fec_mr, now );
     886           0 : }
     887             : 
     888             : static void
     889             : handle_net( fd_repair_tile_t *  ctx,
     890             :             fd_stem_context_t * stem,
     891             :             ulong               sz,
     892           0 :             long                rx_ts ) {
     893           0 :   uchar *        data;
     894           0 :   ulong          data_sz;
     895           0 :   fd_ip4_hdr_t * ip4;
     896           0 :   fd_udp_hdr_t * udp;
     897           0 :   if( FD_UNLIKELY( !fd_ip4_udp_hdr_strip( ctx->net_buf, sz, &data, &data_sz, NULL, &ip4, &udp ) ) ) return;
     898           0 :   if( FD_UNLIKELY( data_sz<sizeof(uint)                                                          ) ) return;
     899             : 
     900           0 :   long now = fd_clock_tile_now( ctx->clock );
     901           0 :   if( FD_UNLIKELY( data_sz==FD_REPAIR_PING_DE_SZ ) ) { /* no response has a ping's size */
     902           0 :     handle_ping( ctx, stem, data, data_sz, ip4, udp, now );
     903           0 :     return;
     904           0 :   }
     905           0 :   switch( FD_LOAD( uint, data ) ) {
     906           0 :   case FD_REPAIR_SERDE_TAG_PARENT_FEC_SET_COUNT_RES: handle_parent_and_fec_set_count_res( ctx, data, data_sz, ip4, udp, now, rx_ts ); break;
     907           0 :   case FD_REPAIR_SERDE_TAG_FEC_SET_ROOT_RES:         handle_fec_set_root_res            ( ctx, data, data_sz, ip4, udp, now, rx_ts ); break;
     908           0 :   default: break;
     909           0 :   }
     910           0 : }
     911             : 
     912             : /* handle_sign sends the message that was waiting for this signature. */
     913             : 
     914             : static void
     915             : handle_sign( fd_repair_tile_t *  ctx,
     916             :              fd_stem_context_t * stem,
     917             :              ulong               in_idx,
     918           0 :              ulong               sig ) {
     919           0 :   ctx->sign[ ctx->in[ in_idx ].sign_idx ].free++;
     920           0 :   sign_t * pending = sign_pool_ele( ctx->sign_pool, sig>>32 );
     921           0 :   memcpy( pending->buf+pending->sig_off, ctx->sign_buf, FD_ED25519_SIG_SZ );
     922             : 
     923           0 :   uchar *             packet = fd_chunk_to_laddr( ctx->net_out_mem, ctx->net_out_chunk );
     924           0 :   fd_ip4_udp_hdrs_t * hdr    = (fd_ip4_udp_hdrs_t *)packet;
     925           0 :   *hdr = *ctx->net_hdr;
     926           0 :   hdr->ip4->saddr       = pending->saddr;
     927           0 :   hdr->ip4->daddr       = pending->daddr;
     928           0 :   hdr->ip4->net_id      = fd_ushort_bswap( ctx->net_id++ );
     929           0 :   hdr->ip4->net_tot_len = fd_ushort_bswap( (ushort)( pending->sz+sizeof(fd_ip4_hdr_t)+sizeof(fd_udp_hdr_t) ) );
     930           0 :   hdr->ip4->check       = fd_ip4_hdr_check_fast( hdr->ip4 );
     931           0 :   hdr->udp->net_dport   = pending->dport;
     932           0 :   hdr->udp->net_len     = fd_ushort_bswap( (ushort)( pending->sz+sizeof(fd_udp_hdr_t) ) );
     933           0 :   memcpy( packet+sizeof(fd_ip4_udp_hdrs_t), pending->buf, pending->sz );
     934             : 
     935           0 :   ulong sz = pending->sz+sizeof(fd_ip4_udp_hdrs_t);
     936           0 :   ulong ts = fd_frag_meta_ts_comp( fd_tickcount() );
     937           0 :   fd_stem_publish( stem, ctx->net_out_idx, fd_disco_netmux_sig( pending->daddr, pending->dport, pending->daddr, DST_PROTO_OUTGOING, sizeof(fd_ip4_udp_hdrs_t) ), ctx->net_out_chunk, sz, 0UL, ts, ts );
     938           0 :   ctx->net_out_chunk = fd_dcache_compact_next( ctx->net_out_chunk, sz, ctx->net_out_chunk0, ctx->net_out_wmark );
     939           0 :   sign_pool_ele_release( ctx->sign_pool, pending );
     940           0 :   ctx->metrics->pkt_tx++;
     941           0 : }
     942             : 
     943             : static inline void
     944           0 : metrics_write( fd_repair_tile_t * ctx ) {
     945           0 :   long now = fd_clock_tile_now( ctx->clock );
     946           0 :   long due = now;
     947           0 :   due = fd_long_if( !!timeout_prq_cnt( ctx->eager_prq ), fd_long_min( due, ctx->eager_prq[ 0 ].timeout ), due );
     948           0 :   due = fd_long_if( !!timeout_prq_cnt( ctx->notar_prq ), fd_long_min( due, ctx->notar_prq[ 0 ].timeout ), due );
     949           0 :   due = fd_long_if( !!timeout_prq_cnt( ctx->final_prq ), fd_long_min( due, ctx->final_prq[ 0 ].timeout ), due );
     950           0 :   ulong queued  = fd_repair_treap_ele_cnt( ctx->repair->eager_treap ) + fd_repair_treap_ele_cnt( ctx->repair->notar_treap ) + fd_repair_treap_ele_cnt( ctx->repair->final_treap );
     951           0 :   ulong waiting = timeout_prq_cnt( ctx->eager_prq ) + timeout_prq_cnt  ( ctx->notar_prq ) + timeout_prq_cnt( ctx->final_prq );
     952             : 
     953           0 :   FD_MCNT_SET( REPAIR, PKT_TX,                               ctx->metrics->pkt_tx                                                      );
     954           0 :   FD_MCNT_SET( REPAIR, REQUEST_TX_WINDOW_INDEX,              ctx->metrics->request_tx[ FD_REPAIR_SERDE_TAG_WINDOW_INDEX ]              );
     955           0 :   FD_MCNT_SET( REPAIR, REQUEST_TX_HIGHEST_WINDOW_INDEX,      ctx->metrics->request_tx[ FD_REPAIR_SERDE_TAG_HIGHEST_WINDOW_INDEX ]      );
     956           0 :   FD_MCNT_SET( REPAIR, REQUEST_TX_ORPHAN,                    ctx->metrics->request_tx[ FD_REPAIR_SERDE_TAG_ORPHAN ]                    );
     957           0 :   FD_MCNT_SET( REPAIR, REQUEST_TX_PARENT_FEC_SET_COUNT,      ctx->metrics->request_tx[ FD_REPAIR_SERDE_TAG_PARENT_AND_FEC_SET_COUNT ]  );
     958           0 :   FD_MCNT_SET( REPAIR, REQUEST_TX_FEC_SET_ROOT,              ctx->metrics->request_tx[ FD_REPAIR_SERDE_TAG_FEC_SET_ROOT ]              );
     959           0 :   FD_MCNT_SET( REPAIR, REQUEST_TX_WINDOW_INDEX_FOR_BLOCK_ID, ctx->metrics->request_tx[ FD_REPAIR_SERDE_TAG_WINDOW_INDEX_FOR_BLOCK_ID ] );
     960           0 :   FD_MCNT_SET( REPAIR, REQUEST_TX_PONG,                      ctx->metrics->request_tx[ FD_REPAIR_SERDE_TAG_PONG ]                      );
     961           0 :   FD_MCNT_SET( REPAIR, PING_PROBE_TX,                        ctx->metrics->ping_tx                                                     );
     962             : 
     963           0 :   FD_MGAUGE_SET( REPAIR, HIGHEST_DELIVERED_SLOT, ctx->metrics->slot_highest_repaired                 );
     964           0 :   FD_MGAUGE_SET( REPAIR, HIGHEST_RECEIVED_SLOT,  ctx->metrics->slot_current                          );
     965           0 :   FD_MGAUGE_SET( REPAIR, FIRST_TURBINE_SLOT,     ctx->turbine_slot0                                  );
     966           0 :   FD_MGAUGE_SET( REPAIR, CATCHUP_SLOT,           ctx->catchup_slot                                   );
     967           0 :   FD_MGAUGE_SET( REPAIR, BLOCK_PENDING,          queued                                              );
     968           0 :   FD_MGAUGE_SET( REPAIR, REQUEST_IN_FLIGHT,      pending_pool_used( ctx->pending_pool )              );
     969           0 :   FD_MGAUGE_SET( REPAIR, TIMEOUT_PENDING,        waiting                                             );
     970           0 :   FD_MGAUGE_SET( REPAIR, EAGER_DELAY_NANOS,      (ulong)fd_repair_strat_eager_ns( ctx->strat, NULL ) );
     971           0 :   FD_MGAUGE_SET( REPAIR, SIGN_REQUEST_PENDING,   sign_pool_used( ctx->sign_pool )                    );
     972           0 :   FD_MGAUGE_SET( REPAIR, TIMEOUT_OVERDUE_NANOS,  (ulong)( now-due )                                  );
     973           0 :   FD_MGAUGE_SET( REPAIR, FIRST_CERT_SLOT,        ctx->cert_slot0                                     );
     974             : 
     975           0 :   FD_MCNT_SET( REPAIR, FEC_DELIVERED,            ctx->metrics->fec_delivered );
     976           0 :   FD_MCNT_SET( REPAIR, TIMEOUT_CANCELLED,        ctx->metrics->req_cancelled );
     977           0 :   FD_MCNT_SET( REPAIR, TIMEOUT_NO_PEER_DEFERRED, ctx->metrics->req_no_peer   );
     978           0 :   FD_MCNT_SET( REPAIR, REQUEST_HEDGED,           ctx->metrics->req_hedged    );
     979           0 :   FD_MCNT_SET( REPAIR, REQUEST_EXPIRED,          ctx->metrics->req_expired   );
     980           0 :   FD_MCNT_SET( REPAIR, REQUEST_BY_ID_EXPIRED,    ctx->metrics->byid_expired      );
     981           0 :   FD_MCNT_SET( REPAIR, REQUEST_STUCK_ESCAPED,    ctx->metrics->req_stuck_escaped );
     982           0 :   FD_MCNT_SET( REPAIR, REQUEST_LATE_RETRIED,     ctx->metrics->req_late_attempt  );
     983           0 :   FD_MCNT_SET( REPAIR, PEER_BY_ID_BANNED,        ctx->metrics->peer_byid_banned  );
     984             : 
     985           0 :   FD_MCNT_SET      ( REPAIR, TIMEOUT_NO_STAKED_PEER_DEFERRED, ctx->metrics->req_no_staked_peer );
     986           0 :   FD_MCNT_ENUM_COPY( REPAIR, TIMEOUT_DEFERRED,                ctx->metrics->timeout_deferred   );
     987           0 :   FD_MCNT_SET      ( REPAIR, TIMEOUT_QUEUE_FULL_DROPPED,      ctx->metrics->timeout_full       );
     988           0 :   FD_MCNT_SET      ( REPAIR, TIMEOUT_QUEUE_FULL_PAUSED,       ctx->metrics->timeout_paused     );
     989           0 :   FD_MCNT_SET      ( REPAIR, SIGNING_HALTED,                  ctx->metrics->sign_halted        );
     990             : 
     991           0 :   FD_MCNT_SET      ( REPAIR, SHRED_OLD_IGNORED, ctx->metrics->shred_old );
     992           0 :   FD_MCNT_ENUM_COPY( REPAIR, SHRED_RX,          ctx->metrics->shred_rx  );
     993             : 
     994           0 :   FD_MCNT_SET( REPAIR, META_RX,                          ctx->metrics->meta_rx                 );
     995           0 :   FD_MCNT_SET( REPAIR, META_MALFORMED_REJECTED,          ctx->metrics->meta_malformed          );
     996           0 :   FD_MCNT_SET( REPAIR, META_UNSOLICITED_REJECTED,        ctx->metrics->meta_unsolicited        );
     997           0 :   FD_MCNT_SET( REPAIR, PARENT_FEC_SET_COUNT_ACCEPTED,     ctx->metrics->parent_fec_count_ok     );
     998           0 :   FD_MCNT_SET( REPAIR, PARENT_FEC_SET_COUNT_FAILED,       ctx->metrics->parent_fec_count_failed );
     999           0 :   FD_MCNT_SET( REPAIR, FEC_SET_ROOT_ACCEPTED,       ctx->metrics->fec_root_ok             );
    1000           0 :   FD_MCNT_SET( REPAIR, FEC_SET_ROOT_FAILED,         ctx->metrics->fec_root_failed         );
    1001             : 
    1002           0 :   FD_MCNT_SET( REPAIR, REPLAY_ROOT_ADVANCED_RX, ctx->metrics->replay_root_advanced );
    1003           0 :   FD_MCNT_SET( REPAIR, REPLAY_MISSING_FEC_RX,   ctx->metrics->replay_missing_fec   );
    1004             : 
    1005           0 :   FD_MCNT_SET( REPAIR, PING_REJECTED_MALFORMED,        ctx->metrics->ping_malformed        );
    1006           0 :   FD_MCNT_SET( REPAIR, PING_REJECTED_UNKNOWN_PEER,     ctx->metrics->ping_unknown_peer     );
    1007           0 :   FD_MCNT_SET( REPAIR, PING_REJECTED_SIGNATURE_FAILED, ctx->metrics->ping_signature_failed );
    1008           0 :   FD_MCNT_SET( REPAIR, PING_REJECTED_SIGN_UNAVAILABLE, ctx->metrics->ping_sign_unavailable );
    1009           0 :   FD_MCNT_SET( REPAIR, PING_REJECTED_ADDRESS_MISMATCH, ctx->metrics->ping_address_mismatch );
    1010           0 :   FD_MCNT_SET( REPAIR, PING_REJECTED_UNSOLICITED,      ctx->metrics->ping_unsolicited      );
    1011             : 
    1012           0 :   FD_MHIST_COPY( REPAIR, RESPONSE_LATENCY_NANOS, ctx->metrics->response_latency );
    1013           0 :   FD_MHIST_COPY( REPAIR, RETRY_DELAY_NANOS,      ctx->metrics->retry_delay      );
    1014           0 :   FD_MHIST_COPY( REPAIR, ESCAPE_PEER_HEDGE_DELAY_NANOS, ctx->metrics->escape_hedge );
    1015           0 : }
    1016             : 
    1017             : FD_FN_CONST static inline ulong
    1018           0 : scratch_align( void ) {
    1019           0 :   return 128UL;
    1020           0 : }
    1021             : 
    1022             : FD_FN_PURE static inline ulong
    1023           0 : scratch_footprint( fd_topo_tile_t const * tile ) {
    1024           0 :   ulong sign_max    = tile->repair.repair_sign_depth*tile->repair.repair_sign_cnt;
    1025           0 :   ulong timeout_max = tile->repair.slot_max*(FEC_SET_P90+1UL); /* one blk of the role per slot, each a FEC set req for its p90 FEC sets and a HIGHEST_WINDOW_INDEX or PARENT_AND_FEC_SET_COUNT, a full prq rewinds its blk */
    1026           0 :   ulong pending_max = tile->repair.repair_sign_cnt*(ulong)( PENDING_TTL/SIGN_NS ); /* what the sign tiles can sign within PENDING_TTL, each pending is at least one */
    1027           0 :   ulong chain_cnt   = pending_map_chain_cnt_est( pending_max );
    1028           0 :   ulong l = FD_LAYOUT_INIT;
    1029           0 :   l = FD_LAYOUT_APPEND( l, alignof(fd_repair_tile_t),          sizeof(fd_repair_tile_t)                         );
    1030           0 :   l = FD_LAYOUT_APPEND( l, fd_repair_align(),                  fd_repair_footprint( tile->repair.slot_max, tile->repair.fec_max, tile->repair.max_shreds_per_block/FD_FEC_SHRED_CNT ) );
    1031           0 :   l = FD_LAYOUT_APPEND( l, fd_repair_strat_align(),            fd_repair_strat_footprint()                      );
    1032           0 :   l = FD_LAYOUT_APPEND( l, fd_multi_epoch_leaders_align(),     fd_multi_epoch_leaders_footprint()               );
    1033           0 :   l = FD_LAYOUT_APPEND( l, ping_deque_align(),                 ping_deque_footprint( FD_REPAIR_STRAT_PEER_MAX ) );
    1034           0 :   l = FD_LAYOUT_APPEND( l, timeout_prq_align(),                timeout_prq_footprint( timeout_max )             );
    1035           0 :   l = FD_LAYOUT_APPEND( l, timeout_prq_align(),                timeout_prq_footprint( timeout_max )             );
    1036           0 :   l = FD_LAYOUT_APPEND( l, timeout_prq_align(),                timeout_prq_footprint( timeout_max )             );
    1037           0 :   l = FD_LAYOUT_APPEND( l, pending_pool_align(),               pending_pool_footprint( pending_max )            );
    1038           0 :   l = FD_LAYOUT_APPEND( l, pending_map_align(),                pending_map_footprint( chain_cnt )               );
    1039           0 :   l = FD_LAYOUT_APPEND( l, pending_dlist_align(),              pending_dlist_footprint()                        );
    1040           0 :   l = FD_LAYOUT_APPEND( l, sign_pool_align(),                  sign_pool_footprint( sign_max )                  );
    1041           0 :   l = FD_LAYOUT_APPEND( l, alignof(fd_event_block_received_t), sizeof(fd_event_block_received_t)                );
    1042           0 :   return FD_LAYOUT_FINI( l, scratch_align() );
    1043           0 : }
    1044             : 
    1045             : static inline void
    1046             : after_credit( fd_repair_tile_t *  ctx,
    1047             :               fd_stem_context_t * stem,
    1048             :               int *               opt_poll_in,
    1049           0 :               int *               charge_busy ) {
    1050             : 
    1051             :   /* Send one reassembled FEC to replay, then repair, which publishes
    1052             :      only on unreliable links. */
    1053             : 
    1054           0 :   if( FD_LIKELY( !fd_repair_deque_empty( ctx->repair->reasm_deque ) ) ) {
    1055           0 :     *opt_poll_in = 0;
    1056             : 
    1057           0 :     fd_repair_deque_t       out     = fd_repair_deque_pop_head( ctx->repair->reasm_deque );
    1058           0 :     fd_repair_blk_t const * blk     = fd_repair_blk_pool_ele_const( ctx->repair->blk_pool, out.blk_idx );
    1059           0 :     fd_repair_fec_t const * fec     = fd_repair_fec_pool_ele_const( ctx->repair->fec_pool, blk->fecs[ out.fec_idx ] );
    1060           0 :     fd_repair_out_fec_t *   out_fec = fd_chunk_to_laddr( ctx->replay_out_mem, ctx->replay_out_chunk );
    1061           0 :     memset( out_fec, 0, sizeof(fd_repair_out_fec_t) );
    1062           0 :     out_fec->slot                  = blk->slot;
    1063           0 :     out_fec->fec_set_idx           = out.fec_idx*FD_FEC_SHRED_CNT;
    1064           0 :     out_fec->mr                    = FD_LOAD( fd_hash_t, fec->mr.mr32.uc );
    1065           0 :     out_fec->parent_slot           = blk->parent_slot;
    1066           0 :     out_fec->parent_block_id       = FD_LOAD( fd_hash_t, blk->parent_dmr.uc );
    1067           0 :     out_fec->update_parent_fec_idx = blk->init_fec_cnt*FD_FEC_SHRED_CNT;
    1068           0 :     out_fec->slot_complete         = out.fec_idx+1U==blk->cmpl_fec_cnt;
    1069           0 :     out_fec->data_complete         = fec->data_complete;
    1070           0 :     out_fec->is_leader             = fec->is_leader;
    1071           0 :     out_fec->is_known              = fd_repair_slot_meta( ctx->repair, blk->slot )->eager_blk_idx!=out.blk_idx;
    1072           0 :     out_fec->block_id              = FD_LOAD( fd_hash_t, blk->dmr.uc );
    1073           0 :     out_fec->is_finalized          = out_fec->slot_complete && fd_repair_slot_meta( ctx->repair, blk->slot )->final_blk_idx==out.blk_idx;
    1074           0 :     out_fec->highest_fec_complete_slot = fd_ulong_max( ctx->highest_fec_complete_slot, ctx->catchup_slot );
    1075           0 :     out_fec->fec_completed_ts_nanos    = fec->cmpl_ts;       /* shred tile rx of the shred that completed it, 0 for our own */
    1076           0 :     out_fec->votor_repaired            = (uchar)out_fec->is_known;
    1077           0 :     fd_stem_publish( stem, ctx->replay_out_idx, REPAIR_SIG_FEC, ctx->replay_out_chunk, sizeof(fd_repair_out_fec_t), 0UL, 0UL, fd_frag_meta_ts_comp( fd_tickcount() ) );
    1078           0 :     ctx->replay_out_chunk = fd_dcache_compact_next( ctx->replay_out_chunk, sizeof(fd_repair_out_fec_t), ctx->replay_out_chunk0, ctx->replay_out_wmark );
    1079           0 :     ctx->metrics->fec_delivered++;
    1080           0 :     ctx->metrics->slot_highest_repaired = fd_ulong_if( out_fec->slot_complete, fd_ulong_max( ctx->metrics->slot_highest_repaired, blk->slot ), ctx->metrics->slot_highest_repaired );
    1081             : 
    1082           0 :     if( FD_UNLIKELY( out_fec->slot_complete && ctx->rserve_out_idx!=ULONG_MAX && blk->cmpl_fec_cnt<=FD_FEC_BLK_MAX ) ) { /* rserve serves protocol sized blocks only */
    1083           0 :       fd_repair_out_blk_t * out_blk = fd_chunk_to_laddr( ctx->rserve_out_mem, ctx->rserve_out_chunk );
    1084           0 :       out_blk->slot                 = blk->slot;
    1085           0 :       out_blk->block_id             = FD_LOAD( fd_hash_t, blk->dmr.uc );
    1086           0 :       out_blk->parent_slot          = blk->parent_slot;
    1087           0 :       out_blk->parent_block_id      = FD_LOAD( fd_hash_t, blk->parent_dmr.uc );
    1088           0 :       out_blk->fec_set_cnt          = blk->cmpl_fec_cnt;
    1089           0 :       for( uint k=0U; k<blk->cmpl_fec_cnt; k++ ) memcpy( out_blk->merkle_roots[ k ], fd_repair_fec_pool_ele_const( ctx->repair->fec_pool, blk->fecs[ k ] )->mr.mr20.uc, FD_SHRED_MERKLE_NODE_SZ );
    1090           0 :       ulong sz = offsetof(fd_repair_out_blk_t, merkle_roots) + (ulong)blk->cmpl_fec_cnt*FD_SHRED_MERKLE_NODE_SZ;
    1091           0 :       fd_stem_publish( stem, ctx->rserve_out_idx, REPAIR_SIG_BLK, ctx->rserve_out_chunk, sz, 0UL, 0UL, fd_frag_meta_ts_comp( fd_tickcount() ) );
    1092           0 :       ctx->rserve_out_chunk = fd_dcache_compact_next( ctx->rserve_out_chunk, sz, ctx->rserve_out_chunk0, ctx->rserve_out_wmark );
    1093           0 :     }
    1094           0 :     *charge_busy = 1;
    1095           0 :   }
    1096             : 
    1097             :   /* Then expire unanswered requests, discover as many blks off the
    1098             :      blk_treaps as there are free sign slots (a blk yields at least one
    1099             :      req, a req at least one sign request), and send pings and due
    1100             :      reqs while sign slots and pending_pool entries last.  A req is one
    1101             :      peer pick. */
    1102             : 
    1103           0 :   long now = fd_clock_tile_now( ctx->clock );
    1104             : 
    1105           0 :   while( !pending_dlist_is_empty( ctx->pending_dlist, ctx->pending_pool ) ) {
    1106           0 :     pending_t * pending = pending_dlist_ele_peek_head( ctx->pending_dlist, ctx->pending_pool );
    1107           0 :     if( FD_LIKELY( now-pending->ts<PENDING_TTL ) ) break;
    1108             : 
    1109             :     /* No rtt sample: silence also means a deduped reply, or a highest
    1110             :        or orphan request no peer can answer yet.  Only a run of shred
    1111             :        and meta requests left unanswered bans the peer.  A request
    1112             :        answered in part settled its pick on the first shred. */
    1113             : 
    1114           0 :     int answerable = pending->slot>ctx->repair->root && pending->tag!=FD_REPAIR_SERDE_TAG_HIGHEST_WINDOW_INDEX && pending->tag!=FD_REPAIR_SERDE_TAG_ORPHAN;
    1115           0 :     int byid = pending->tag==FD_REPAIR_SERDE_TAG_WINDOW_INDEX_FOR_BLOCK_ID;
    1116           0 :     ctx->metrics->byid_expired += (ulong)( answerable && byid && !pending->answered );
    1117           0 :     int ban = 0;
    1118           0 :     if(      FD_UNLIKELY( pending->answered ) ) {}
    1119           0 :     else if( FD_LIKELY  ( answerable        ) ) ban = fd_repair_strat_request_expired( ctx->strat, &pending->peer, now+BAN_TTL, byid );
    1120           0 :     else                                        fd_repair_strat_request_failed ( ctx->strat, &pending->peer, 0           );
    1121           0 :     ctx->metrics->peer_byid_banned += (ulong)( ban==1 );
    1122           0 :     fd_repair_blk_t const * blk        = answerable || !ctx->report_repair_requests || !fd_event_tl ? NULL : fd_repair_blk_map_ele_query_const( ctx->repair->blk_map, &pending->slot, NULL, ctx->repair->blk_pool );
    1123           0 :     while( blk && fd_repair_blk_pool_idx( ctx->repair->blk_pool, blk )!=pending->blk ) blk = fd_repair_blk_map_ele_next_const( blk, NULL, ctx->repair->blk_pool );
    1124           0 :     fd_repair_blk_t const * parent     = blk ? fd_repair_blk_pool_ele_const( ctx->repair->blk_pool, blk->parent ) : NULL;
    1125           0 :     int                     superseded = pending->slot>ctx->repair->root && blk && ( ( pending->tag==FD_REPAIR_SERDE_TAG_HIGHEST_WINDOW_INDEX && blk->cmpl_fec_cnt ) || ( pending->tag==FD_REPAIR_SERDE_TAG_ORPHAN && parent && parent->parent_slot!=ULONG_MAX ) );
    1126           0 :     int                     outcome    = answerable ? FD_EVENT_REPAIR_REQUEST_OUTCOME_EXPIRED : superseded ? FD_EVENT_REPAIR_REQUEST_OUTCOME_SUPERSEDED : FD_EVENT_REPAIR_REQUEST_OUTCOME_UNANSWERABLE;
    1127           0 :     int                     ban_kind   = fd_int_if( ban==1, FD_EVENT_REPAIR_REQUEST_BAN_BY_ID, fd_int_if( ban==-1, FD_EVENT_REPAIR_REQUEST_BAN_STREAK, FD_EVENT_REPAIR_REQUEST_BAN_NONE ) );
    1128           0 :     for( uint m=pending->mask; m; m=fd_uint_pop_lsb( m ) ) report_repair_request( ctx, pending, (uint)fd_uint_find_lsb( m ), 0L, outcome, ban_kind );
    1129           0 :     ctx->metrics->req_expired+= (ulong)!pending->answered;
    1130           0 :     pending_dlist_ele_pop_head ( ctx->pending_dlist, ctx->pending_pool );
    1131           0 :     pending_map_ele_remove_fast( ctx->pending_map,   pending, ctx->pending_pool );
    1132           0 :     pending_pool_ele_release   ( ctx->pending_pool,  pending );
    1133           0 :   }
    1134             : 
    1135           0 :   for( ulong d=sign_pool_free(  ctx->sign_pool ); d; d-- ) {
    1136           0 :     fd_repair_t *       repair = ctx->repair;
    1137           0 :     fd_repair_treap_t * treap  = fd_repair_treap_ele_cnt( repair->final_treap ) ? repair->final_treap :
    1138           0 :                                  fd_repair_treap_ele_cnt( repair->notar_treap ) ? repair->notar_treap :
    1139           0 :                                  fd_repair_treap_ele_cnt( repair->eager_treap ) ? repair->eager_treap :
    1140           0 :                                                                                   NULL;
    1141           0 :     if( FD_UNLIKELY( !treap ) ) break;
    1142           0 :     fd_repair_blk_t * blk = fd_repair_treap_fwd_iter_ele( fd_repair_treap_fwd_iter_init( treap, repair->blk_pool ), repair->blk_pool );
    1143           0 :     fd_repair_treap_ele_remove( treap, blk, repair->blk_pool );
    1144           0 :     discover( ctx, blk, now );
    1145           0 :   }
    1146           0 :   if( FD_UNLIKELY( ctx->halt_signing ) ) return;
    1147             : 
    1148             :   /* A ping is any signed request: the peer pings before it answers.  No
    1149             :      one has slot 0 under the null block id, so no one answers it. */
    1150             : 
    1151           0 :   ulong sent = 0UL;
    1152           0 :   ulong ts   = (ulong)now/1000000UL;
    1153           0 :   while(    sent<PING_MAX
    1154           0 :          && !ping_deque_empty( ctx->ping_deque )
    1155           0 :          && sign_pool_free(  ctx->sign_pool )>SIGN_RESERVE ) {
    1156           0 :     fd_pubkey_t              id_key = ping_deque_pop_head( ctx->ping_deque );
    1157           0 :     fd_repair_strat_peer_t * peer   = fd_repair_strat_query( ctx->strat, &id_key );
    1158           0 :     sent++;
    1159           0 :     if( FD_UNLIKELY( !peer || !peer->ip4 || peer->ping_ts ) ) continue; /* gone, or sent to since */
    1160           0 :     peer->ping_ts = now;
    1161             : 
    1162           0 :     sign_t * pending = sign_pool_ele_acquire( ctx->sign_pool );
    1163           0 :     *pending = (sign_t){ .sig_off = sizeof(uint), .daddr = peer->ip4, .dport = fd_ushort_bswap( peer->port ) };
    1164           0 :     uint nonce = (uint)fd_chacha_rng_ulong( ctx->nonce_rng );
    1165           0 :     pending->sz = fd_repair_req_parent_and_fec_set_count_ser( &(fd_repair_parent_fec_set_count_t){ .slot = 0UL }, sig_null, &ctx->identity_key, &id_key, ts, nonce, pending->buf );
    1166             : 
    1167           0 :     ulong s = 0UL;
    1168           0 :     for( ulong i=1UL; i<ctx->sign_cnt; i++ ) s = fd_ulong_if( ctx->sign[ i ].free>ctx->sign[ s ].free, i, s );
    1169           0 :     ulong sz = fd_repair_req_sig_ser( pending->buf, pending->sz, fd_chunk_to_laddr( ctx->sign[ s ].mem, ctx->sign[ s ].chunk ) );
    1170           0 :     fd_stem_publish( stem, ctx->sign[ s ].idx, (sign_pool_idx( ctx->sign_pool, pending )<<32)|(ulong)FD_KEYGUARD_SIGN_TYPE_ED25519, ctx->sign[ s ].chunk, sz, 0UL, 0UL, 0UL );
    1171           0 :     ctx->sign[ s ].chunk = fd_dcache_compact_next( ctx->sign[ s ].chunk, sz, ctx->sign[ s ].chunk0, ctx->sign[ s ].wmark );
    1172           0 :     ctx->sign[ s ].free--;
    1173           0 :     ctx->metrics->ping_tx++;
    1174           0 :     *charge_busy = 1;
    1175           0 :   }
    1176             : 
    1177           0 :   while( sign_pool_free( ctx->sign_pool )>=SIGN_RESERVE+FD_FEC_SHRED_CNT ) {
    1178           0 :     timeout_t         req;
    1179           0 :     fd_repair_blk_t * blk = poll_timeout( ctx, now, &req );
    1180           0 :     if( FD_UNLIKELY(  !blk ) ) break;
    1181             : 
    1182           0 :     fd_repair_fec_t const * fec   = fd_repair_fec_pool_ele_const( ctx->repair->fec_pool, fd_ulong_if( req.tag==FD_REPAIR_SERDE_TAG_WINDOW_INDEX, blk->fecs[ req.fec_idx ], fd_repair_fec_pool_idx_null( ctx->repair->fec_pool ) ) );
    1183           0 :     int                     eager = blk->eager;
    1184           0 :     uint                    tag   = req.tag!=FD_REPAIR_SERDE_TAG_WINDOW_INDEX ? req.tag                                      : /* a FEC set req becomes what its blk still lacks */
    1185           0 :                                     eager                                     ? FD_REPAIR_SERDE_TAG_WINDOW_INDEX             :
    1186           0 :                                     !fec                                      ? FD_REPAIR_SERDE_TAG_FEC_SET_ROOT             :
    1187           0 :                                                                                 FD_REPAIR_SERDE_TAG_WINDOW_INDEX_FOR_BLOCK_ID;
    1188           0 :     int                    meta  = tag==FD_REPAIR_SERDE_TAG_PARENT_AND_FEC_SET_COUNT || tag==FD_REPAIR_SERDE_TAG_FEC_SET_ROOT;
    1189           0 :     uint                   mask  = fd_uint_if( tag==FD_REPAIR_SERDE_TAG_WINDOW_INDEX || tag==FD_REPAIR_SERDE_TAG_WINDOW_INDEX_FOR_BLOCK_ID, fec ? fd_uint_if( fec->fec_complete, 0U, ~fec->data_rcvd ) : UINT_MAX, 1U ); /* bit i is a message for shred i of the FEC set */
    1190           0 :     if( FD_UNLIKELY( !mask ) ) { /* every shred arrived, the FEC set completes without repair */
    1191           0 :       ctx->metrics->timeout_deferred[ FD_METRICS_ENUM_TIMEOUT_DEFER_REASON_V_ALL_RECEIVED_IDX ]++;
    1192           0 :       req.timeout = now+fd_repair_strat_eager_ns( ctx->strat, fd_multi_epoch_leaders_get_leader_for_slot( ctx->mleaders, blk->slot ) );
    1193           0 :       push_timeout( ctx, blk, &req );
    1194           0 :       continue;
    1195           0 :     }
    1196             : 
    1197             :     /* A retry asks someone else.  After STUCK_MAX attempts that brought
    1198             :        no new shred, the fastest peers are not answering: pick a staked
    1199             :        peer at random instead. */
    1200           0 :     uint rcvd   = fec ? fd_uint_if( fec->fec_complete, UINT_MAX, fec->data_rcvd ) : 0U;
    1201           0 :     uint stuck  = fd_uint_if( req.attempt && !( rcvd & ~req.rcvd ), req.stuck+1U, 0U ); /* only new shreds are progress, not an eviction; kept only once sent */
    1202           0 :     int  escape = stuck>=STUCK_MAX && !blk->eager;
    1203           0 :     fd_repair_strat_peer_t * peer = escape ? fd_repair_strat_pick_staked( ctx->strat, now, (ulong)req.last_peer-1UL, fd_rng_ulong( ctx->rng ) ) : NULL;
    1204           0 :     int  escaped = !!peer;
    1205           0 :     ctx->metrics->req_stuck_escaped += (ulong)!!peer;
    1206           0 :     if( FD_UNLIKELY( peer ) ) fd_histf_sample( ctx->metrics->escape_hedge, (ulong)fd_long_if( !!peer->srtt_ns, peer->srtt_ns+4L*peer->rttvar_ns, FD_REPAIR_STRAT_HEDGE_NS ) ); /* uncapped */
    1207           0 :     if( FD_LIKELY( !peer ) ) peer = fd_repair_strat_pick( ctx->strat, now, !blk->eager && req.attempt, (ulong)req.last_peer-1UL );
    1208           0 :     if( FD_UNLIKELY( !peer ) ) {
    1209           0 :       ctx->metrics->req_no_peer++;
    1210           0 :       ctx->metrics->req_no_staked_peer += (ulong)( !blk->eager && req.attempt );
    1211           0 :       req.timeout = now+NO_PEER;
    1212           0 :       push_timeout( ctx, blk, &req );
    1213           0 :       break;
    1214           0 :     }
    1215           0 :     peer->ping_ts = now;
    1216           0 :     req.stuck     = stuck;
    1217           0 :     req.rcvd      = rcvd;
    1218             : 
    1219           0 :     uint nonce = (uint)fd_chacha_rng_ulong( ctx->nonce_rng );
    1220           0 :     while( meta && pending_map_ele_query_const( ctx->pending_map, &(ulong){ nonce }, NULL, ctx->pending_pool ) ) {
    1221           0 :       nonce = (uint)fd_chacha_rng_ulong( ctx->nonce_rng );
    1222           0 :     }
    1223             : 
    1224           0 :     if( FD_UNLIKELY( !pending_pool_free( ctx->pending_pool ) ) ) { /* evict the oldest, a reply to it no longer matches */
    1225           0 :       pending_t * oldest = pending_dlist_ele_pop_head( ctx->pending_dlist, ctx->pending_pool );
    1226           0 :       if( FD_LIKELY( !oldest->answered ) ) fd_repair_strat_request_failed( ctx->strat, &oldest->peer, 0 );
    1227           0 :       for( uint m=oldest->mask; m; m=fd_uint_pop_lsb( m ) ) report_repair_request( ctx, oldest, (uint)fd_uint_find_lsb( m ), 0L, FD_EVENT_REPAIR_REQUEST_OUTCOME_EVICTED, FD_EVENT_REPAIR_REQUEST_BAN_NONE );
    1228           0 :       pending_map_ele_remove_fast( ctx->pending_map, oldest, ctx->pending_pool );
    1229           0 :       pending_pool_ele_release   ( ctx->pending_pool, oldest );
    1230           0 :     }
    1231           0 :     fd_repair_slot_meta_t const * slot_meta = fd_repair_slot_meta( ctx->repair, blk->slot );
    1232           0 :     int                           role      = slot_meta->final_blk_idx==req.blk ? FD_EVENT_REPAIR_REQUEST_ROLE_FINAL : slot_meta->notar_blk_idx==req.blk ? FD_EVENT_REPAIR_REQUEST_ROLE_NOTAR : FD_EVENT_REPAIR_REQUEST_ROLE_EAGER;
    1233           0 :     int                           pick      = escaped ? FD_EVENT_REPAIR_REQUEST_PICK_ESCAPE : !req.attempt ? FD_EVENT_REPAIR_REQUEST_PICK_FIRST : !blk->eager ? FD_EVENT_REPAIR_REQUEST_PICK_STAKED_HEDGE : FD_EVENT_REPAIR_REQUEST_PICK_HEDGE;
    1234           0 :     pending_t * pending = pending_pool_ele_acquire( ctx->pending_pool );
    1235           0 :     pending->key         = fd_ulong_if( meta, (ulong)nonce, (ulong)(uint)blk->slot<<32 | fd_uint_if( req.tag==FD_REPAIR_SERDE_TAG_HIGHEST_WINDOW_INDEX || req.tag==FD_REPAIR_SERDE_TAG_ORPHAN, (uint)ctx->repair->fec_blk_max, req.fec_idx ) ); /* an ORPHAN's first reply is of the slot itself, matched like a HIGHEST */
    1236           0 :     pending->ts          = now;
    1237           0 :     pending->slot        = blk->slot;
    1238           0 :     pending->blk         = req.blk;
    1239           0 :     pending->dmr         = FD_LOAD( fd_hash_t, blk->dmr.uc );
    1240           0 :     pending->peer        = peer->id_key;
    1241           0 :     pending->fec_idx     = fd_uint_if( tag==FD_REPAIR_SERDE_TAG_HIGHEST_WINDOW_INDEX, blk->rcvd_fec_cnt, req.fec_idx );
    1242           0 :     pending->fec_set_cnt = blk->cmpl_fec_cnt;
    1243           0 :     pending->tag         = tag;
    1244           0 :     pending->mask        = mask;
    1245           0 :     pending->answered    = 0U;
    1246           0 :     pending->ip4         = peer->ip4;
    1247           0 :     pending->port        = peer->port;
    1248           0 :     pending->attempt     = req.attempt;
    1249           0 :     pending->pick        = (uchar)pick;
    1250           0 :     pending->role        = (uchar)role;
    1251           0 :     pending_map_ele_insert     ( ctx->pending_map,   pending, ctx->pending_pool );
    1252           0 :     pending_dlist_ele_push_tail( ctx->pending_dlist, pending, ctx->pending_pool );
    1253             : 
    1254           0 :     for( uint m=mask; m; m=fd_uint_pop_lsb( m ) ) {
    1255           0 :       uint             idx          = req.fec_idx*FD_FEC_SHRED_CNT+(uint)fd_uint_find_lsb( m );
    1256           0 :       uint             rnonce       = fd_uint_if( meta, nonce, fd_rnonce_ss_compute( ctx->rnonce_ss, tag!=FD_REPAIR_SERDE_TAG_HIGHEST_WINDOW_INDEX && tag!=FD_REPAIR_SERDE_TAG_ORPHAN, blk->slot, idx, now ) );
    1257           0 :       sign_t *         sign         = sign_pool_ele_acquire( ctx->sign_pool );
    1258           0 :       *sign = (sign_t){ .sig_off = sizeof(uint), .daddr = peer->ip4, .dport = fd_ushort_bswap( peer->port ) };
    1259           0 :       switch( tag ) {
    1260           0 :       case FD_REPAIR_SERDE_TAG_PARENT_AND_FEC_SET_COUNT:  sign->sz = fd_repair_req_parent_and_fec_set_count_ser ( &(fd_repair_parent_fec_set_count_t){ .slot = blk->slot, .block_id = FD_LOAD( fd_hash_t, blk->dmr.uc )                     }, sig_null, &ctx->identity_key, &peer->id_key, ts, rnonce, sign->buf ); break;
    1261           0 :       case FD_REPAIR_SERDE_TAG_FEC_SET_ROOT:              sign->sz = fd_repair_req_fec_set_root_ser             ( &(fd_repair_fec_set_root_t){ .slot = blk->slot, .block_id = FD_LOAD( fd_hash_t, blk->dmr.uc ), .fec_set_idx = idx }, sig_null, &ctx->identity_key, &peer->id_key, ts, rnonce, sign->buf ); break;
    1262           0 :       case FD_REPAIR_SERDE_TAG_ORPHAN:                    sign->sz = fd_repair_req_orphan_ser                   ( &(fd_repair_orphan_t){ .slot = blk->slot                                               }, sig_null, &ctx->identity_key, &peer->id_key, ts, rnonce, sign->buf ); break;
    1263           0 :       case FD_REPAIR_SERDE_TAG_HIGHEST_WINDOW_INDEX:      sign->sz = fd_repair_req_highest_window_index_ser     ( &(fd_repair_highest_shred_t){ .slot = blk->slot, .shred_idx = blk->rcvd_fec_cnt*FD_FEC_SHRED_CNT }, sig_null, &ctx->identity_key, &peer->id_key, ts, rnonce, sign->buf ); break;
    1264           0 :       case FD_REPAIR_SERDE_TAG_WINDOW_INDEX:              sign->sz = fd_repair_req_window_index_ser             ( &(fd_repair_shred_t){ .slot = blk->slot, .shred_idx = idx                                 }, sig_null, &ctx->identity_key, &peer->id_key, ts, rnonce, sign->buf ); break;
    1265           0 :       case FD_REPAIR_SERDE_TAG_WINDOW_INDEX_FOR_BLOCK_ID: sign->sz = fd_repair_req_window_index_for_block_id_ser( &(fd_repair_shred_for_block_id_t){ .slot = blk->slot, .shred_idx = idx, .block_id = FD_LOAD( fd_hash_t, blk->dmr.uc ) }, sig_null, &ctx->identity_key, &peer->id_key, ts, rnonce, sign->buf ); break;
    1266           0 :       }
    1267             : 
    1268           0 :       ulong s = 0UL;
    1269           0 :       for( ulong i=1UL; i<ctx->sign_cnt; i++ ) s = fd_ulong_if( ctx->sign[ i ].free>ctx->sign[ s ].free, i, s );
    1270           0 :       ulong sz = fd_repair_req_sig_ser( sign->buf, sign->sz, fd_chunk_to_laddr( ctx->sign[ s ].mem, ctx->sign[ s ].chunk ) );
    1271           0 :       fd_stem_publish( stem, ctx->sign[ s ].idx, (sign_pool_idx( ctx->sign_pool, sign )<<32)|(ulong)FD_KEYGUARD_SIGN_TYPE_ED25519, ctx->sign[ s ].chunk, sz, 0UL, 0UL, 0UL );
    1272           0 :       ctx->sign[ s ].chunk = fd_dcache_compact_next( ctx->sign[ s ].chunk, sz, ctx->sign[ s ].chunk0, ctx->sign[ s ].wmark );
    1273           0 :       ctx->sign[ s ].free--;
    1274           0 :     }
    1275           0 :     ctx->metrics->request_tx[ tag ] += (ulong)fd_uint_popcnt( mask );
    1276           0 :     ctx->metrics->req_hedged        += (ulong)!!req.attempt;
    1277           0 :     ctx->metrics->req_late_attempt  += (ulong)( req.attempt>=3U );
    1278             : 
    1279           0 :     blk->telemetry.first_req_ts      = fd_long_if( !blk->telemetry.first_req_ts, now, blk->telemetry.first_req_ts );
    1280           0 :     uint * req_cnt = NULL;
    1281           0 :     switch( tag ) {
    1282           0 :     case FD_REPAIR_SERDE_TAG_WINDOW_INDEX:              req_cnt = &blk->telemetry.req_window_cnt;    break;
    1283           0 :     case FD_REPAIR_SERDE_TAG_HIGHEST_WINDOW_INDEX:      req_cnt = &blk->telemetry.req_highest_cnt;   break;
    1284           0 :     case FD_REPAIR_SERDE_TAG_ORPHAN:                    req_cnt = &blk->telemetry.req_orphan_cnt;    break;
    1285           0 :     case FD_REPAIR_SERDE_TAG_WINDOW_INDEX_FOR_BLOCK_ID: req_cnt = &blk->telemetry.req_shred_bid_cnt; break;
    1286           0 :     case FD_REPAIR_SERDE_TAG_PARENT_AND_FEC_SET_COUNT:  req_cnt = &blk->telemetry.req_parent_cnt;    break;
    1287           0 :     case FD_REPAIR_SERDE_TAG_FEC_SET_ROOT:              req_cnt = &blk->telemetry.req_fec_root_cnt;  break;
    1288           0 :     }
    1289           0 :     if( FD_LIKELY( req_cnt ) ) *req_cnt += (uint)fd_uint_popcnt( mask );
    1290             : 
    1291           0 :     long hedge  = fd_long_if( escape, 1L<<24, fd_long_min( fd_long_max( fd_repair_strat_hedge_ns( peer ), 1L<<24 ), FD_REPAIR_STRAT_HEDGE_NS ) );
    1292           0 :     req.timeout = now+hedge; /* no backoff: a re-pick gets a new shred nonce, and a random escape pick does not slow the next one */
    1293           0 :     req.attempt   = (uchar)fd_uint_min( req.attempt+1U, UCHAR_MAX );
    1294           0 :     req.last_peer = (ushort)( 1UL+(ulong)( peer-ctx->strat->cur.peers ) );
    1295           0 :     fd_histf_sample( ctx->metrics->retry_delay, (ulong)( req.timeout-now ) );
    1296           0 :     push_timeout( ctx, blk, &req );
    1297           0 :     *charge_busy = 1;
    1298           0 :   }
    1299           0 : }
    1300             : 
    1301             : static int
    1302             : before_frag( fd_repair_tile_t * ctx,
    1303             :              ulong              in_idx,
    1304             :              ulong              seq FD_PARAM_UNUSED,
    1305           0 :              ulong              sig ) {
    1306           0 :   switch( ctx->in_kind[ in_idx  ] ) {
    1307           0 :   case IN_KIND_GENESIS:
    1308           0 :     return 0;
    1309           0 :   case IN_KIND_SHRED: {
    1310           0 :     if( FD_UNLIKELY( ctx->repair->root==ULONG_MAX ) ) return -1; /* hold until the snapshot or genesis gives the root */
    1311           0 :     if( FD_LIKELY( fd_shred_sig_src( sig )!=SHRED_SIG_FUTURE ) ) return 0;
    1312           0 :     ulong slot = fd_shred_sig_future_slot( sig );
    1313           0 :     if( FD_LIKELY( ctx->staked || slot<ctx->repair->root+ctx->repair->slot_max || slot<=ctx->catchup_slot ) ) return 1;
    1314           0 :     ctx->catchup_slot = slot;
    1315           0 :     fd_repair_slot_catchup( ctx->repair, ctx->repair->root+ctx->repair->slot_max );
    1316           0 :     return 1;
    1317           0 :   }
    1318           0 :   case IN_KIND_SNAP: {
    1319           0 :     ulong msg = fd_ssmsg_sig_message( sig );
    1320           0 :     return msg!=FD_SSMSG_MANIFEST_FULL && msg!=FD_SSMSG_MANIFEST_INCREMENTAL && msg!=FD_SSMSG_DONE;
    1321           0 :   }
    1322           0 :   case IN_KIND_VOTOR:
    1323           0 :     if( FD_UNLIKELY( ctx->repair->root==ULONG_MAX ) ) return -1; /* hold until the snapshot or genesis gives the root */
    1324           0 :     return sig!=FD_VOTOR_SIG_CERTED && sig!=FD_VOTOR_SIG_REPAIR && sig!=FD_VOTOR_SIG_QUORUM;
    1325           0 :   case IN_KIND_REPLAY:
    1326           0 :     return sig!=REPLAY_SIG_ROOT_ADVANCED && sig!=REPLAY_SIG_SLOT_DEAD && sig!=REPLAY_SIG_MISSING_FEC;
    1327           0 :   case IN_KIND_EPOCH:
    1328           0 :     return 0;
    1329           0 :   case IN_KIND_GOSSIP:
    1330           0 :     return sig!=FD_GOSSIP_UPDATE_TAG_CONTACT_INFO && sig!=FD_GOSSIP_UPDATE_TAG_CONTACT_INFO_REMOVE;
    1331           0 :   case IN_KIND_NET:
    1332           0 :     return fd_disco_netmux_sig_proto( sig )!=DST_PROTO_REPAIR;
    1333           0 :   case IN_KIND_SIGN:
    1334           0 :     return 0;
    1335           0 :   default:
    1336           0 :     FD_LOG_ERR(( "unexpected in_kind %d", ctx->in_kind[ in_idx ] ));
    1337           0 :   }
    1338           0 : }
    1339             : 
    1340             : static void
    1341             : during_frag( fd_repair_tile_t * ctx,
    1342             :              ulong              in_idx,
    1343             :              ulong              seq FD_PARAM_UNUSED,
    1344             :              ulong              sig FD_PARAM_UNUSED,
    1345             :              ulong              chunk,
    1346             :              ulong              sz,
    1347           0 :              ulong              ctl FD_PARAM_UNUSED ) {
    1348           0 :   switch( ctx->in_kind[ in_idx  ] ) {
    1349           0 :   case IN_KIND_NET:
    1350           0 :     if( FD_UNLIKELY( chunk<ctx->in[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz>ctx->in[ in_idx ].mtu ) ) {
    1351           0 :       FD_LOG_ERR(( "chunk %lu sz %lu from in_kind %d out of bounds, chunk0 %lu wmark %lu",
    1352           0 :                    chunk, sz, ctx->in_kind[ in_idx ], ctx->in[ in_idx ].chunk0, ctx->in[ in_idx ].wmark ));
    1353           0 :     }
    1354           0 :     memcpy( ctx->net_buf, fd_chunk_to_laddr_const( ctx->in[ in_idx ].mem, chunk ), sz );
    1355           0 :     break;
    1356           0 :   case IN_KIND_SIGN:
    1357           0 :     if( FD_UNLIKELY( chunk<ctx->in[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz>ctx->in[ in_idx ].mtu ) ) {
    1358           0 :       FD_LOG_ERR(( "chunk %lu sz %lu from in_kind %d out of bounds, chunk0 %lu wmark %lu",
    1359           0 :                    chunk, sz, ctx->in_kind[ in_idx ], ctx->in[ in_idx ].chunk0, ctx->in[ in_idx ].wmark ));
    1360           0 :     }
    1361           0 :     memcpy( ctx->sign_buf, fd_chunk_to_laddr_const( ctx->in[ in_idx ].mem, chunk ), FD_ED25519_SIG_SZ );
    1362           0 :     break;
    1363           0 :   case IN_KIND_REPLAY:
    1364           0 :     if( FD_UNLIKELY( sz!=0UL && ( chunk<ctx->in[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz>sizeof(ctx->replay_buf) ) ) ) { /* a missing FEC has no payload */
    1365           0 :       FD_LOG_ERR(( "chunk %lu sz %lu from in_kind %d out of bounds, chunk0 %lu wmark %lu",
    1366           0 :                    chunk, sz, ctx->in_kind[ in_idx ], ctx->in[ in_idx ].chunk0, ctx->in[ in_idx ].wmark ));
    1367           0 :     }
    1368           0 :     memcpy( &ctx->replay_buf, fd_chunk_to_laddr_const( ctx->in[ in_idx ].mem, chunk ), sz );
    1369           0 :     break;
    1370           0 :   default: /* reliable, read in place by after_frag */
    1371           0 :     if( FD_UNLIKELY( sz!=0UL && ( chunk<ctx->in[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz>ctx->in[ in_idx ].mtu ) ) ) { /* a snapshot DONE has no payload */
    1372           0 :       FD_LOG_ERR(( "chunk %lu sz %lu from in_kind %d out of bounds, chunk0 %lu wmark %lu",
    1373           0 :                    chunk, sz, ctx->in_kind[ in_idx ], ctx->in[ in_idx ].chunk0, ctx->in[ in_idx ].wmark ));
    1374           0 :     }
    1375           0 :     ctx->chunk = chunk;
    1376           0 :     break;
    1377           0 :   }
    1378           0 : }
    1379             : 
    1380             : static void
    1381             : after_frag( fd_repair_tile_t *  ctx,
    1382             :             ulong               in_idx,
    1383             :             ulong               seq    FD_PARAM_UNUSED,
    1384             :             ulong               sig,
    1385             :             ulong               sz,
    1386             :             ulong               tsorig,
    1387             :             ulong               tspub,
    1388           0 :             fd_stem_context_t * stem ) {
    1389           0 :   uchar const * src = fd_chunk_to_laddr_const( ctx->in[ in_idx ].mem, ctx->chunk );
    1390             : 
    1391           0 :   switch( ctx->in_kind[ in_idx ] ) {
    1392           0 :   case IN_KIND_NET: {
    1393           0 :     long now_tick = fd_tickcount();
    1394           0 :     long rx_tick  = fd_frag_meta_ts_decomp( tspub, now_tick );
    1395           0 :     if( FD_UNLIKELY( rx_tick>now_tick ) ) rx_tick -= 1L<<32;
    1396           0 :     handle_net( ctx, stem, sz, fd_clock_tile_tickcount_to_wallclock( ctx->clock, rx_tick ) );
    1397           0 :     break;
    1398           0 :   }
    1399           0 :   case IN_KIND_SIGN:
    1400           0 :     handle_sign( ctx, stem, in_idx, sig );
    1401           0 :     break;
    1402           0 :   case IN_KIND_GENESIS: {
    1403           0 :     fd_genesis_meta_t const * meta = (fd_genesis_meta_t const *)fd_type_pun_const( src );
    1404           0 :     if( FD_UNLIKELY( !meta->bootstrap ) ) break; /* booting from a snapshot */
    1405           0 :     fd_repair_init( ctx->repair, 0UL, fd_type_pun_const( &hash_null ), report_block_received, ctx );
    1406           0 :     break;
    1407           0 :   }
    1408           0 :   case IN_KIND_SHRED: { /* tsorig is the shred tile's network rx of the shred, the completing one for a FEC set */
    1409           0 :     long now_tick = fd_tickcount();
    1410           0 :     long rx_tick  = fd_frag_meta_ts_decomp( tsorig, now_tick );
    1411           0 :     if( FD_UNLIKELY( rx_tick>now_tick ) ) rx_tick -= 1L<<32;
    1412           0 :     handle_shred( ctx, sig, src, fd_clock_tile_tickcount_to_wallclock( ctx->clock, rx_tick ) );
    1413           0 :     break;
    1414           0 :   }
    1415           0 :   case IN_KIND_SNAP:
    1416           0 :     handle_snapshot( ctx, sig, src );
    1417           0 :     break;
    1418           0 :   case IN_KIND_VOTOR:
    1419           0 :     handle_votor( ctx, sig, src );
    1420           0 :     break;
    1421           0 :   case IN_KIND_REPLAY:
    1422           0 :     handle_replay( ctx, sig, (uchar const *)&ctx->replay_buf );
    1423           0 :     break;
    1424           0 :   case IN_KIND_EPOCH: {
    1425           0 :     fd_epoch_info_msg_t const * msg = (fd_epoch_info_msg_t const *)fd_type_pun_const( src );
    1426           0 :     fd_repair_strat_epoch_advanced( ctx->strat, fd_epoch_info_msg_id_weights( msg ), msg->staked_id_cnt );
    1427           0 :     timeout_t * prqs[3] = { ctx->final_prq, ctx->notar_prq, ctx->eager_prq };
    1428           0 :     for( ulong c=0UL; c<3UL; c++ ) { /* the re-sort moved peers, keep retries avoiding the same one */
    1429           0 :       for( ulong i=0UL; i<timeout_prq_cnt( prqs[ c ] ); i++ ) {
    1430           0 :         timeout_t * req = &prqs[ c ][ i ];
    1431           0 :         req->last_peer = (ushort)( 1UL+fd_repair_strat_slot_remap( ctx->strat, (ulong)req->last_peer-1UL ) );
    1432           0 :       }
    1433           0 :     }
    1434           0 :     fd_repair_strat_peer_t const * self = fd_repair_strat_query( ctx->strat, &ctx->identity_key );
    1435           0 :     ctx->staked = self && (ulong)( self-ctx->strat->cur.peers )<ctx->strat->staked_cnt;
    1436           0 :     fd_multi_epoch_leaders_epoch_msg_init( ctx->mleaders, msg );
    1437           0 :     fd_multi_epoch_leaders_epoch_msg_fini( ctx->mleaders );
    1438           0 :     break;
    1439           0 :   }
    1440           0 :   case IN_KIND_GOSSIP:
    1441           0 :     handle_gossip( ctx, sig, (fd_gossip_update_message_t const *)fd_type_pun_const( src ) );
    1442           0 :     break;
    1443           0 :   default:
    1444           0 :     FD_LOG_ERR(( "unexpected in_kind %d", ctx->in_kind[ in_idx ] ));
    1445           0 :   }
    1446           0 : }
    1447             : 
    1448             : /* next_deadline is when the tile next has work without a frag: the
    1449             :    earliest timeout due, or the oldest pending expiring. */
    1450             : 
    1451             : static long
    1452           0 : next_deadline( fd_repair_tile_t * ctx ) {
    1453           0 :   long due = LONG_MAX;
    1454           0 :   due = fd_long_if( !!timeout_prq_cnt( ctx->eager_prq ), fd_long_min( due, ctx->eager_prq[ 0 ].timeout ), due );
    1455           0 :   due = fd_long_if( !!timeout_prq_cnt( ctx->notar_prq ), fd_long_min( due, ctx->notar_prq[ 0 ].timeout ), due );
    1456           0 :   due = fd_long_if( !!timeout_prq_cnt( ctx->final_prq ), fd_long_min( due, ctx->final_prq[ 0 ].timeout ), due );
    1457           0 :   if( FD_LIKELY( !pending_dlist_is_empty( ctx->pending_dlist, ctx->pending_pool ) ) ) due = fd_long_min( due, pending_dlist_ele_peek_head( ctx->pending_dlist, ctx->pending_pool )->ts+PENDING_TTL );
    1458           0 :   if( FD_UNLIKELY( due==LONG_MAX ) ) return LONG_MAX;
    1459           0 :   return fd_clock_tile_wallclock_to_tickcount( ctx->clock, due );
    1460           0 : }
    1461             : 
    1462             : static inline void
    1463           0 : during_housekeeping( fd_repair_tile_t * ctx ) {
    1464           0 :   if( FD_UNLIKELY( fd_clock_tile_recal_due( ctx->clock ) ) ) fd_clock_tile_recal( ctx->clock );
    1465             : 
    1466           0 :   if( FD_UNLIKELY( fd_keyswitch_state_query( ctx->keyswitch )==FD_KEYSWITCH_STATE_UNHALT_PENDING ) ) {
    1467           0 :     FD_CHECK_CRIT( ctx->halt_signing, "state machine corruption" );
    1468           0 :     ctx->halt_signing = 0;
    1469             : 
    1470             :     /* Peers key their ping-pong cache by our pubkey, so every peer is
    1471             :        cold for the new identity.  Re-warm them all. */
    1472           0 :     ping_deque_remove_all( ctx->ping_deque );
    1473           0 :     for( ulong i=0UL; i<FD_REPAIR_STRAT_PEER_MAX && !ping_deque_full( ctx->ping_deque ); i++ ) {
    1474           0 :       fd_repair_strat_peer_t * peer = &ctx->strat->cur.peers[ i ];
    1475           0 :       if( FD_LIKELY( !peer->ip4 ) ) continue; /* free slot, or no address */
    1476           0 :       peer->ping_ts = 0L;
    1477           0 :       ping_deque_push_tail( ctx->ping_deque, peer->id_key );
    1478           0 :     }
    1479             : 
    1480           0 :     fd_keyswitch_state( ctx->keyswitch, FD_KEYSWITCH_STATE_COMPLETED );
    1481           0 :   }
    1482             : 
    1483           0 :   if( FD_UNLIKELY( fd_keyswitch_state_query( ctx->keyswitch )==FD_KEYSWITCH_STATE_SWITCH_PENDING ) ) {
    1484           0 :     ctx->metrics->sign_halted += (ulong)!ctx->halt_signing;
    1485           0 :     ctx->halt_signing = 1;
    1486           0 :     memcpy( ctx->identity_key.uc, ctx->keyswitch->bytes, 32UL );
    1487           0 :     fd_repair_strat_peer_t const * self = fd_repair_strat_query( ctx->strat, &ctx->identity_key );
    1488           0 :     ctx->staked = self && (ulong)( self-ctx->strat->cur.peers )<ctx->strat->staked_cnt;
    1489           0 :     if( FD_LIKELY( sign_pool_free( ctx->sign_pool )==sign_pool_max( ctx->sign_pool ) ) ) fd_keyswitch_state( ctx->keyswitch, FD_KEYSWITCH_STATE_COMPLETED ); /* every sign request returned */
    1490           0 :   }
    1491           0 : }
    1492             : 
    1493             : static void
    1494             : privileged_init( fd_topo_t const *      topo,
    1495           0 :                  fd_topo_tile_t const * tile ) {
    1496           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
    1497             : 
    1498           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
    1499           0 :   fd_repair_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_repair_tile_t), sizeof(fd_repair_tile_t) );
    1500           0 :   fd_memset( ctx, 0, sizeof(fd_repair_tile_t) );
    1501             : 
    1502           0 :   FD_TEST( fd_rng_secure( &ctx->seed, sizeof(ulong) ) );
    1503           0 :   uchar nonce_seed[ 32 ];
    1504           0 :   FD_TEST( fd_rng_secure( nonce_seed, sizeof(nonce_seed) ) );
    1505           0 :   FD_TEST( fd_chacha_rng_join( fd_chacha_rng_new( ctx->nonce_rng, FD_CHACHA_RNG_MODE_SHIFT ) ) );
    1506           0 :   fd_chacha_rng_init( ctx->nonce_rng, nonce_seed, FD_CHACHA_RNG_ALGO_CHACHA8 );
    1507           0 :   fd_memzero_explicit( nonce_seed, sizeof(nonce_seed) );
    1508             : 
    1509           0 :   uchar const * identity_key = fd_keyload_load( tile->repair.identity_key_path, /* pubkey only: */ 1 );
    1510           0 :   fd_memcpy( ctx->identity_key.uc, identity_key, sizeof(fd_pubkey_t) );
    1511             : 
    1512           0 :   ulong rnonce_ss_id = fd_pod_queryf_ulong( topo->props, ULONG_MAX, "rnonce_ss" );
    1513           0 :   FD_TEST( rnonce_ss_id!=ULONG_MAX );
    1514           0 :   memcpy( ctx->rnonce_ss, fd_topo_obj_laddr( topo, rnonce_ss_id ), sizeof(fd_rnonce_ss_t) );
    1515           0 : }
    1516             : 
    1517             : static void
    1518             : unprivileged_init( fd_topo_t const *      topo,
    1519           0 :                    fd_topo_tile_t const * tile ) {
    1520           0 :   void * scratch   = fd_topo_obj_laddr( topo, tile->tile_obj_id );
    1521           0 :   ulong  sign_max    = tile->repair.repair_sign_depth*tile->repair.repair_sign_cnt;
    1522           0 :   ulong  timeout_max = tile->repair.slot_max*(FEC_SET_P90+1UL);
    1523           0 :   ulong  pending_max = tile->repair.repair_sign_cnt*(ulong)( PENDING_TTL/SIGN_NS );
    1524           0 :   ulong  chain_cnt   = pending_map_chain_cnt_est( pending_max );
    1525             : 
    1526           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
    1527           0 :   fd_repair_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_repair_tile_t),          sizeof(fd_repair_tile_t)                         );
    1528           0 :   ctx->repair            = FD_SCRATCH_ALLOC_APPEND( l, fd_repair_align(),                  fd_repair_footprint( tile->repair.slot_max, tile->repair.fec_max, tile->repair.max_shreds_per_block/FD_FEC_SHRED_CNT ) );
    1529           0 :   void * strat           = FD_SCRATCH_ALLOC_APPEND( l, fd_repair_strat_align(),            fd_repair_strat_footprint()                      );
    1530           0 :   void * mleaders        = FD_SCRATCH_ALLOC_APPEND( l, fd_multi_epoch_leaders_align(),     fd_multi_epoch_leaders_footprint()               );
    1531           0 :   void * ping_deque      = FD_SCRATCH_ALLOC_APPEND( l, ping_deque_align(),                 ping_deque_footprint( FD_REPAIR_STRAT_PEER_MAX ) );
    1532           0 :   void * eager_prq       = FD_SCRATCH_ALLOC_APPEND( l, timeout_prq_align(),                timeout_prq_footprint( timeout_max )             );
    1533           0 :   void * notar_prq       = FD_SCRATCH_ALLOC_APPEND( l, timeout_prq_align(),                timeout_prq_footprint( timeout_max )             );
    1534           0 :   void * final_prq       = FD_SCRATCH_ALLOC_APPEND( l, timeout_prq_align(),                timeout_prq_footprint( timeout_max )             );
    1535           0 :   void * pending_pool    = FD_SCRATCH_ALLOC_APPEND( l, pending_pool_align(),               pending_pool_footprint( pending_max )            );
    1536           0 :   void * pending_map     = FD_SCRATCH_ALLOC_APPEND( l, pending_map_align(),                pending_map_footprint( chain_cnt )               );
    1537           0 :   void * pending_dlist   = FD_SCRATCH_ALLOC_APPEND( l, pending_dlist_align(),              pending_dlist_footprint()                        );
    1538           0 :   void * sign_pool       = FD_SCRATCH_ALLOC_APPEND( l, sign_pool_align(),                  sign_pool_footprint( sign_max )                  );
    1539           0 :   ctx->event             = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_event_block_received_t), sizeof(fd_event_block_received_t)                );
    1540           0 :   ulong scratch_top = FD_SCRATCH_ALLOC_FINI( l, scratch_align() );
    1541           0 :   if( FD_UNLIKELY( scratch_top > (ulong)scratch + scratch_footprint( tile ) ) )
    1542           0 :     FD_LOG_ERR(( "scratch overflow %lu %lu %lu", scratch_top - (ulong)scratch - scratch_footprint( tile ), scratch_top, (ulong)scratch + scratch_footprint( tile ) ));
    1543             : 
    1544           0 :   ctx->repair = fd_repair_join( fd_repair_new( ctx->repair, tile->repair.slot_max, tile->repair.fec_max, tile->repair.max_shreds_per_block/FD_FEC_SHRED_CNT, ctx->seed ) );
    1545           0 :   FD_TEST( ctx->repair );
    1546           0 :   ctx->mleaders = fd_multi_epoch_leaders_join( fd_multi_epoch_leaders_new( mleaders ) );
    1547           0 :   FD_TEST( ctx->mleaders );
    1548             : 
    1549           0 :   ctx->keyswitch = fd_keyswitch_join( fd_topo_obj_laddr( topo, tile->id_keyswitch_obj_id ) );
    1550           0 :   FD_TEST( ctx->keyswitch );
    1551             : 
    1552           0 :   ctx->strat         = fd_repair_strat_join( fd_repair_strat_new( strat, ctx->seed ) );
    1553           0 :   ctx->ping_deque    = ping_deque_join( ping_deque_new( ping_deque, FD_REPAIR_STRAT_PEER_MAX ) );
    1554           0 :   ctx->eager_prq     = timeout_prq_join( timeout_prq_new( eager_prq, timeout_max ) );
    1555           0 :   ctx->notar_prq     = timeout_prq_join( timeout_prq_new( notar_prq, timeout_max ) );
    1556           0 :   ctx->final_prq     = timeout_prq_join( timeout_prq_new( final_prq, timeout_max ) );
    1557           0 :   ctx->pending_pool  = pending_pool_join( pending_pool_new( pending_pool, pending_max ) );
    1558           0 :   ctx->pending_map   = pending_map_join( pending_map_new( pending_map, chain_cnt, ctx->seed ) );
    1559           0 :   ctx->pending_dlist = pending_dlist_join( pending_dlist_new( pending_dlist ) );
    1560           0 :   ctx->sign_pool     = sign_pool_join( sign_pool_new( sign_pool, sign_max ) );
    1561           0 :   FD_TEST( ctx->strat && ctx->ping_deque && ctx->eager_prq && ctx->notar_prq && ctx->final_prq && ctx->pending_pool && ctx->pending_map && ctx->pending_dlist && ctx->sign_pool );
    1562           0 :   FD_TEST( fd_rng_join( fd_rng_new( ctx->rng, (uint)ctx->seed, ctx->seed>>32 ) ) );
    1563           0 :   fd_clock_tile_init( ctx->clock );
    1564           0 :   FD_TEST( fd_histf_join( fd_histf_new( ctx->metrics->response_latency, FD_MHIST_MIN( REPAIR, RESPONSE_LATENCY_NANOS ), FD_MHIST_MAX( REPAIR, RESPONSE_LATENCY_NANOS ) ) ) );
    1565           0 :   FD_TEST( fd_histf_join( fd_histf_new( ctx->metrics->retry_delay,      FD_MHIST_MIN( REPAIR, RETRY_DELAY_NANOS      ), FD_MHIST_MAX( REPAIR, RETRY_DELAY_NANOS      ) ) ) );
    1566           0 :   FD_TEST( fd_histf_join( fd_histf_new( ctx->metrics->escape_hedge,     FD_MHIST_MIN( REPAIR, ESCAPE_PEER_HEDGE_DELAY_NANOS ), FD_MHIST_MAX( REPAIR, ESCAPE_PEER_HEDGE_DELAY_NANOS ) ) ) );
    1567             : 
    1568           0 :   FD_TEST( tile->in_cnt<=sizeof(ctx->in_kind)/sizeof(ctx->in_kind[0]) );
    1569           0 :   for( ulong i=0UL; i<tile->in_cnt; i++ ) {
    1570           0 :     fd_topo_link_t const * link = &topo->links[ tile->in_link_id[ i ] ];
    1571             : 
    1572           0 :     if     ( FD_LIKELY( !strcmp( link->name, "genesi_out"    ) ) ) ctx->in_kind[ i ] = IN_KIND_GENESIS;
    1573           0 :     else if( FD_LIKELY( !strcmp( link->name, "shred_out"     ) ) ) ctx->in_kind[ i ] = IN_KIND_SHRED;
    1574           0 :     else if( FD_LIKELY( !strcmp( link->name, "snapin_manif"  ) ) ) ctx->in_kind[ i ] = IN_KIND_SNAP;
    1575           0 :     else if( FD_LIKELY( !strcmp( link->name, "votor_out"     ) ) ) ctx->in_kind[ i ] = IN_KIND_VOTOR;
    1576           0 :     else if( FD_LIKELY( !strcmp( link->name, "shred_repair"  ) ) ) ctx->in_kind[ i ] = IN_KIND_NET;
    1577           0 :     else if( FD_LIKELY( !strcmp( link->name, "gossip_ciaddr" ) ) ) ctx->in_kind[ i ] = IN_KIND_GOSSIP;
    1578           0 :     else if( FD_LIKELY( !strcmp( link->name, "replay_slot"   ) ) ) ctx->in_kind[ i ] = IN_KIND_REPLAY;
    1579           0 :     else if( FD_LIKELY( !strcmp( link->name, "replay_repair" ) ) ) ctx->in_kind[ i ] = IN_KIND_REPLAY;
    1580           0 :     else if( FD_LIKELY( !strcmp( link->name, "sign_repair"   ) ) ) ctx->in_kind[ i ] = IN_KIND_SIGN;
    1581           0 :     else if( FD_LIKELY( !strcmp( link->name, "replay_epoch"  ) ) ) ctx->in_kind[ i ] = IN_KIND_EPOCH;
    1582           0 :     else FD_LOG_ERR(( "repair tile has unexpected input link %lu %s", i, link->name ));
    1583           0 :     ctx->in[ i ].mem    = topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ].wksp;
    1584           0 :     ctx->in[ i ].chunk0 = fd_dcache_compact_chunk0( ctx->in[ i ].mem, link->dcache );
    1585           0 :     ctx->in[ i ].wmark  = fd_dcache_compact_wmark ( ctx->in[ i ].mem, link->dcache, link->mtu );
    1586           0 :     ctx->in[ i ].mtu    = link->mtu;
    1587           0 :   }
    1588             : 
    1589           0 :   ctx->replay_out_idx = fd_topo_find_tile_out_link( topo, tile, "repair_out", 0UL );
    1590           0 :   FD_TEST( ctx->replay_out_idx!=ULONG_MAX );
    1591           0 :   fd_topo_link_t const * replay_out = &topo->links[ tile->out_link_id[ ctx->replay_out_idx ] ];
    1592           0 :   ctx->replay_out_mem    = topo->workspaces[ topo->objs[ replay_out->dcache_obj_id ].wksp_id ].wksp;
    1593           0 :   ctx->replay_out_chunk0 = fd_dcache_compact_chunk0( ctx->replay_out_mem, replay_out->dcache );
    1594           0 :   ctx->replay_out_wmark  = fd_dcache_compact_wmark ( ctx->replay_out_mem, replay_out->dcache, replay_out->mtu );
    1595           0 :   ctx->replay_out_chunk  = ctx->replay_out_chunk0;
    1596             : 
    1597             :   /* sign tile s takes repair_sign s and answers on sign_repair s */
    1598             : 
    1599           0 :   ctx->sign_cnt = tile->repair.repair_sign_cnt;
    1600           0 :   FD_TEST( ctx->sign_cnt<=SIGN_MAX );
    1601           0 :   for( ulong s=0UL; s<ctx->sign_cnt; s++ ) {
    1602           0 :     ulong out_idx = fd_topo_find_tile_out_link( topo, tile, "repair_sign", s );
    1603           0 :     ulong in_idx  = fd_topo_find_tile_in_link ( topo, tile, "sign_repair", s );
    1604           0 :     FD_TEST( out_idx!=ULONG_MAX && in_idx!=ULONG_MAX );
    1605           0 :     fd_topo_link_t const * sign_out = &topo->links[ tile->out_link_id[ out_idx ] ];
    1606           0 :     FD_TEST( sign_out->mtu>=FD_REPAIR_SIG_SER_MAX );
    1607           0 :     ctx->sign[ s ].idx         = out_idx;
    1608           0 :     ctx->sign[ s ].mem         = topo->workspaces[ topo->objs[ sign_out->dcache_obj_id ].wksp_id ].wksp;
    1609           0 :     ctx->sign[ s ].chunk0      = fd_dcache_compact_chunk0( ctx->sign[ s ].mem, sign_out->dcache );
    1610           0 :     ctx->sign[ s ].wmark       = fd_dcache_compact_wmark ( ctx->sign[ s ].mem, sign_out->dcache, sign_out->mtu );
    1611           0 :     ctx->sign[ s ].chunk       = ctx->sign[ s ].chunk0;
    1612           0 :     ctx->sign[ s ].free        = tile->repair.repair_sign_depth;
    1613           0 :     ctx->in[ in_idx ].sign_idx = s;
    1614           0 :   }
    1615             : 
    1616           0 :   ctx->net_out_idx = fd_topo_find_tile_out_link( topo, tile, "repair_net", 0UL );
    1617           0 :   FD_TEST( ctx->net_out_idx!=ULONG_MAX );
    1618           0 :   fd_topo_link_t const * net_out = &topo->links[ tile->out_link_id[ ctx->net_out_idx ] ];
    1619           0 :   ctx->net_out_mem    = topo->workspaces[ topo->objs[ net_out->dcache_obj_id ].wksp_id ].wksp;
    1620           0 :   ctx->net_out_chunk0 = fd_dcache_compact_chunk0( ctx->net_out_mem, net_out->dcache );
    1621           0 :   ctx->net_out_wmark  = fd_dcache_compact_wmark ( ctx->net_out_mem, net_out->dcache, net_out->mtu );
    1622           0 :   ctx->net_out_chunk  = ctx->net_out_chunk0;
    1623           0 :   fd_ip4_udp_hdr_init( ctx->net_hdr, 0UL, 0U, tile->repair.repair_client_listen_port );
    1624           0 :   ctx->allow_private_address  = tile->repair.allow_private_address;
    1625           0 :   ctx->report_repair_requests = tile->repair.report_repair_requests;
    1626             : 
    1627           0 :   ctx->rserve_out_idx = fd_topo_find_tile_out_link( topo, tile, "repair_rserve", 0UL );
    1628           0 :   if( FD_LIKELY( ctx->rserve_out_idx!=ULONG_MAX ) ) {
    1629           0 :     fd_topo_link_t const * rserve_out = &topo->links[ tile->out_link_id[ ctx->rserve_out_idx ] ];
    1630           0 :     ctx->rserve_out_mem    = topo->workspaces[ topo->objs[ rserve_out->dcache_obj_id ].wksp_id ].wksp;
    1631           0 :     ctx->rserve_out_chunk0 = fd_dcache_compact_chunk0( ctx->rserve_out_mem, rserve_out->dcache );
    1632           0 :     ctx->rserve_out_wmark  = fd_dcache_compact_wmark ( ctx->rserve_out_mem, rserve_out->dcache, rserve_out->mtu );
    1633           0 :     ctx->rserve_out_chunk  = ctx->rserve_out_chunk0;
    1634           0 :   }
    1635           0 : }
    1636             : 
    1637             : static ulong
    1638             : populate_allowed_seccomp( fd_topo_t const *      topo FD_PARAM_UNUSED,
    1639             :                           fd_topo_tile_t const * tile FD_PARAM_UNUSED,
    1640             :                           ulong                  out_cnt,
    1641           0 :                           struct sock_filter *   out ) {
    1642           0 :   populate_sock_filter_policy_fd_repair_tile( out_cnt, out, (uint)fd_log_private_logfile_fd() );
    1643           0 :   return sock_filter_policy_fd_repair_tile_instr_cnt;
    1644           0 : }
    1645             : 
    1646             : static ulong
    1647             : populate_allowed_fds( fd_topo_t const *      topo FD_PARAM_UNUSED,
    1648             :                       fd_topo_tile_t const * tile FD_PARAM_UNUSED,
    1649             :                       ulong                  out_fds_cnt,
    1650           0 :                       int *                  out_fds ) {
    1651           0 :   if( FD_UNLIKELY( out_fds_cnt<2UL ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
    1652             : 
    1653           0 :   ulong out_cnt = 0UL;
    1654           0 :   out_fds[ out_cnt++ ] = 2; /* stderr */
    1655           0 :   if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) )
    1656           0 :     out_fds[ out_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
    1657           0 :   return out_cnt;
    1658           0 : }
    1659             : 
    1660           0 : #define STEM_BURST (1UL)
    1661           0 : #define STEM_LAZY  (128L*3000L)
    1662             : 
    1663           0 : #define STEM_CALLBACK_CONTEXT_TYPE  fd_repair_tile_t
    1664           0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_repair_tile_t)
    1665             : 
    1666           0 : #define STEM_CALLBACK_METRICS_WRITE       metrics_write
    1667           0 : #define STEM_CALLBACK_DURING_HOUSEKEEPING during_housekeeping
    1668           0 : #define STEM_CALLBACK_NEXT_DEADLINE       next_deadline
    1669           0 : #define STEM_CALLBACK_AFTER_CREDIT        after_credit
    1670           0 : #define STEM_CALLBACK_BEFORE_FRAG         before_frag
    1671           0 : #define STEM_CALLBACK_DURING_FRAG         during_frag
    1672           0 : #define STEM_CALLBACK_AFTER_FRAG          after_frag
    1673             : 
    1674             : #include "../../disco/stem/fd_stem.c"
    1675             : 
    1676             : static ulong
    1677           0 : max_event_sz( fd_topo_tile_t const * tile FD_PARAM_UNUSED ) {
    1678           0 :   return sizeof(fd_event_block_received_t);
    1679           0 : }
    1680             : 
    1681             : fd_topo_run_tile_t fd_tile_repair = {
    1682             :   .name                     = "repair",
    1683             :   .max_event_sz             = max_event_sz,
    1684             :   .populate_allowed_seccomp = populate_allowed_seccomp,
    1685             :   .populate_allowed_fds     = populate_allowed_fds,
    1686             :   .scratch_align            = scratch_align,
    1687             :   .scratch_footprint        = scratch_footprint,
    1688             :   .unprivileged_init        = unprivileged_init,
    1689             :   .privileged_init          = privileged_init,
    1690             :   .run                      = stem_run,
    1691             : };

Generated by: LCOV version 1.14