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

Generated by: LCOV version 1.14