LCOV - code coverage report
Current view: top level - choreo/rotor - fd_rotor.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 25 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 4 0.0 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_choreo_rotor_fd_rotor_h
       2             : #define HEADER_fd_src_choreo_rotor_fd_rotor_h
       3             : 
       4             : #include "../../disco/fd_disco_base.h"
       5             : #include "../../disco/shred/fd_fec_set.h"
       6             : #include "../votor/ag_votor_base.h"
       7             : #include "../fd_choreo_base.h"
       8             : 
       9             : struct fd_mr20 {
      10             :   uchar uc[ FD_SHRED_MERKLE_NODE_SZ ];
      11             : };
      12             : typedef struct fd_mr20 fd_mr20_t;
      13             : 
      14             : typedef fd_hash_t fd_mr32_t;
      15             : 
      16             : /* FD_ROTOR_BLK_VERSION_{NUM,DEN} is the number of blk versions rotor
      17             :    holds per slot of its window, as the fraction NUM/DEN (2.5).  With
      18             :    20% of stake malicious and equivocating AG_EQVOC_BLOCK_HASH_MAX
      19             :    versions of each of its slots, the expected count is 2.2 per slot,
      20             :    2.5 leaves headroom for the variance of the leader schedule (see
      21             :    store_fec_max in src/app/firedancer/topology.c, which sizes Store
      22             :    with the same factor). */
      23             : 
      24           0 : #define FD_ROTOR_BLK_VERSION_NUM (5UL)
      25           0 : #define FD_ROTOR_BLK_VERSION_DEN (2UL)
      26             : 
      27             : /* fd_rotor_blk_max returns the number of blks a rotor with a window of
      28             :    slot_max slots holds, ie. ceil( slot_max*NUM/DEN ). */
      29             : 
      30             : FD_FN_CONST static inline ulong
      31           0 : fd_rotor_blk_max( ulong slot_max ) {
      32           0 :   return ( slot_max*FD_ROTOR_BLK_VERSION_NUM + FD_ROTOR_BLK_VERSION_DEN-1UL ) / FD_ROTOR_BLK_VERSION_DEN;
      33           0 : }
      34             : 
      35             : typedef struct fd_rotor_blk fd_rotor_blk_t;
      36             : 
      37             : struct fd_rotor_fec {
      38             :   fd_mr20_t key;
      39             :   fd_mr32_t mr32;
      40             :   uint      fec_idx;
      41             :   ulong     slot;
      42             :   uint      rcvd;
      43             :   uchar     complete      : 1;
      44             :   uchar     data_complete : 1;
      45             :   uchar     is_leader     : 1;
      46             :   uchar     notarized     : 1;
      47             :   ulong     next;
      48             :   ulong     prev;
      49             :   uint      data_rcvd;     /* shred tile masks at first completion: bit j set if the data shred with in-type index j was received (vs recovered) */
      50             :   uint      parity_rcvd;   /* ... if the parity shred with in-type index j was received */
      51             :   uint      repair_rcvd;   /* ... if the data shred with in-type index j arrived by repair */
      52             : 
      53             :   int       final_src;     /* FD_EVENT_BLOCK_RECEIVED_FEC_SETS_FINAL_SHRED_SOURCE_* of the last data shred received before it completed, 0 if none */
      54             :   long      first_ts;      /* when its first shred arrived, 0 if none yet */
      55             :   long      cmpl_ts;       /* when it first completed, 0 if not yet */
      56             : };
      57             : typedef struct fd_rotor_fec fd_rotor_fec_t;
      58             : FD_STATIC_ASSERT( sizeof(fd_rotor_fec_t)==120UL, fd_rotor_fec );
      59             : 
      60             : #define POOL_NAME fd_rotor_fec_pool
      61           0 : #define POOL_T    fd_rotor_fec_t
      62             : #include "../../util/tmpl/fd_pool.c"
      63             : 
      64             : #define MAP_NAME               fd_rotor_fec_map
      65           0 : #define MAP_ELE_T              fd_rotor_fec_t
      66             : #define MAP_KEY_T              fd_mr20_t
      67           0 : #define MAP_KEY                key
      68           0 : #define MAP_KEY_EQ(k0,k1)      (!memcmp((k0),(k1),sizeof(fd_mr20_t)))
      69           0 : #define MAP_KEY_HASH(key,seed) fd_ulong_hash((seed)^FD_LOAD(ulong,(key)->uc))
      70             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
      71             : #include "../../util/tmpl/fd_map_chain.c"
      72             : 
      73             : struct fd_rotor_blk {
      74             :   ulong     slot;
      75             :   fd_mr32_t dmr;
      76             :   ulong     prev;
      77             :   ulong     next;
      78             : 
      79             :   /* blks form a left-child, right-sibling tree at root. */
      80             : 
      81             :   ulong parent;    /* blk_pool idx of the parent blk, null if not linked */
      82             :   ulong child;     /* blk_pool idx of the first child */
      83             :   ulong sibling;   /* blk_pool idx of the next child of the same parent */
      84             : 
      85             :   uint cmpl_fec_cnt; /* FEC set count of the slot, 0 if not yet known */
      86             :   uint rcvd_fec_cnt; /* one past the highest FEC set received, there can be gaps below */
      87             :   uint buff_fec_cnt; /* FEC sets [0,buff_fec_cnt) are complete */
      88             :   uint cons_fec_cnt; /* FEC sets [0,cons_fec_cnt) are consumed by replay, 0 until the parent is consumed in full */
      89             :   uint wait_fec_cnt; /* FEC sets [0,wait_fec_cnt) are complete or have a repair req waiting, kept by the tile */
      90             : 
      91             :   ulong     parent_slot;
      92             :   fd_mr32_t parent_blk_mr;
      93             :   ulong     orphan_next; /* orphan_map chain, while the blk waits for the blk named by parent_blk_mr */
      94             :   ulong     orphan_prev;
      95             : 
      96             :   /* repair, the treap is the blk's role in its slot_meta */
      97             : 
      98             :   uchar in_blk_treap : 1; /* in its role's treap */
      99             :   uchar eager        : 1; /* the slot's eager blk, and may be repaired by position */
     100             :   uchar meta_req     : 1; /* tile: a META req was made */
     101             :   uchar highest_req  : 1; /* tile: a HIGHEST req was made */
     102             :   uchar orphan_req   : 1; /* tile: an ORPHAN req was made */
     103             : 
     104             :   ulong * fecs; /* fec_pool idx of each FEC set, null if none, fec_blk_max of them */
     105             : 
     106             :   long rcvd_fec_ts; /* when rcvd_fec_cnt last grew */
     107             :   struct {
     108             :     ulong parent;
     109             :     ulong left;
     110             :     ulong right;
     111             :     ulong prio;  /* seeded once at pool creation */
     112             :   } treap;
     113             : 
     114             :   struct {
     115             :     uchar cancelled_reason; /* FD_EVENT_BLOCK_RECEIVED_CANCELLED_REASON_*, 0 if never cancelled */
     116             :     uchar reported;         /* given to the report callback, at completion or when freed */
     117             :     long  cancelled_ts;     /* when cancelled_reason was first set, 0 if never cancelled */
     118             :     uint  last_cmpl_fec_idx;    /* fec_set_idx of the FEC set that most recently completed, UINT_MAX if none */
     119             :     long  last_shred_ts;        /* when the blk became contiguous through a FEC set completion, 0 if it has not */
     120             :     uint  turbine_shred_cnt;    /* turbine data and coding shreds of its FEC sets, as counted by the shred tile */
     121             :     uint  repair_shred_cnt;     /* repair shreds of its FEC sets, as counted by the shred tile */
     122             :     uint  recovered_shred_cnt;  /* shreds of its FEC sets recovered from coding shreds */
     123             : 
     124             :     /* tile: sent requests, by FD_ROTOR_SERDE_TAG_*, a FEC set req counts once per shred asked for */
     125             : 
     126             :     uint  req_window_cnt;
     127             :     uint  req_highest_cnt;
     128             :     uint  req_orphan_cnt;
     129             :     uint  req_shred_bid_cnt;
     130             :     uint  req_parent_cnt;
     131             :     uint  req_fec_root_cnt;
     132             :     long  first_req_ts;         /* tile: when the first req of any kind was sent, 0 if none */
     133             : 
     134             :     /* tile: verified responses to its pending requests */
     135             : 
     136             :     uint  shred_res_cnt;        /* repair shreds matched to a pending window or ShredForBlockId req */
     137             :     uint  parent_res_cnt;       /* ParentAndFecCount responses */
     138             :     uint  fec_root_res_cnt;     /* FecRoot responses */
     139             :     long  last_shred_res_ts;    /* when the last matched repair shred arrived, 0 if none */
     140             :     long  first_meta_res_ts;    /* when the first ParentAndFecCount or FecRoot response arrived, 0 if none */
     141             :   } telemetry;
     142             : };
     143             : typedef struct fd_rotor_blk fd_rotor_blk_t;
     144             : 
     145             : /* blk_map is keyed by slot and holds every blk of a slot. */
     146             : 
     147             : #define POOL_NAME fd_rotor_blk_pool
     148           0 : #define POOL_T    fd_rotor_blk_t
     149             : #include "../../util/tmpl/fd_pool.c"
     150             : 
     151             : #define MAP_NAME  fd_rotor_blk_map
     152           0 : #define MAP_ELE_T fd_rotor_blk_t
     153           0 : #define MAP_KEY   slot
     154             : #define MAP_MULTI 1
     155             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
     156             : #include "../../util/tmpl/fd_map_chain.c"
     157             : 
     158             : /* orphan_map is keyed by parent_blk_mr and holds every unlinked blk
     159             :    that names a parent, ie. parent==null && parent_blk_mr!=hash_null,
     160             :    except the root blk.  The blk named may not be held yet, be held
     161             :    under a different dmr (an eager blk) or never come.  The key is the
     162             :    dmr alone, the parent slot is checked when linking.
     163             : 
     164             :    Each call site of add/remove should gate on the blk's state before
     165             :    its transition, the root blk is never in the map: it keeps the
     166             :    parent_blk_mr it was received with, but waits for nothing. */
     167             : 
     168             : #define MAP_NAME               fd_rotor_orphan_map
     169           0 : #define MAP_ELE_T              fd_rotor_blk_t
     170             : #define MAP_KEY_T              fd_mr32_t
     171           0 : #define MAP_KEY                parent_blk_mr
     172           0 : #define MAP_NEXT               orphan_next
     173           0 : #define MAP_PREV               orphan_prev
     174           0 : #define MAP_KEY_EQ(k0,k1)      (!memcmp((k0),(k1),sizeof(fd_mr32_t)))
     175           0 : #define MAP_KEY_HASH(key,seed) fd_ulong_hash((seed)^FD_LOAD(ulong,(key)->uc))
     176             : #define MAP_MULTI              1
     177             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
     178             : #include "../../util/tmpl/fd_map_chain.c"
     179             : 
     180             : #define TREAP_NAME      fd_rotor_treap
     181             : #define TREAP_T         fd_rotor_blk_t
     182             : #define TREAP_QUERY_T   ulong
     183             : #define TREAP_CMP(q,e)  ((int)((q)>(e)->slot) - (int)((q)<(e)->slot))
     184           0 : #define TREAP_LT(e0,e1) ((e0)->slot<(e1)->slot || ((e0)->slot==(e1)->slot && (e0)<(e1)))
     185           0 : #define TREAP_PARENT    treap.parent
     186           0 : #define TREAP_LEFT      treap.left
     187           0 : #define TREAP_RIGHT     treap.right
     188           0 : #define TREAP_PRIO      treap.prio
     189             : #include "../../util/tmpl/fd_treap.c"
     190             : 
     191             : /* slot_meta is a circular buffer. */
     192             : 
     193             : struct fd_rotor_slot_meta {
     194             :   ulong slot;            /* ULONG_MAX if none */
     195             :   ulong eager;           /* blk_pool idx of the eager blk, null if none */
     196             :   ulong notar;           /* blk_pool idx of the first notar or stronger blk, null if none */
     197             :   ulong final;           /* blk_pool idx of the finalized blk, null if none */
     198             :   uint  invalidated : 1; /* leader misbehaved: equivocated, conflicting parents, bad block header, etc. */
     199             :   uint  skipped     : 1; /* the slot is finalized as skipped */
     200             :   uint  leader      : 1; /* we are the slot's leader */
     201             : };
     202             : typedef struct fd_rotor_slot_meta fd_rotor_slot_meta_t;
     203             : 
     204             : struct fd_rotor_deque {
     205             :   uint blk_idx;
     206             :   uint fec_idx;
     207             : };
     208             : typedef struct fd_rotor_deque fd_rotor_deque_t;
     209             : 
     210             : #define DEQUE_NAME fd_rotor_deque
     211           0 : #define DEQUE_T    fd_rotor_deque_t
     212             : #include "../../util/tmpl/fd_deque_dynamic.c"
     213             : 
     214             : typedef void (* fd_rotor_report_fn_t)( void * ctx, struct fd_rotor_blk const * blk );
     215             : 
     216             : struct fd_rotor {
     217             :   ulong            root;
     218             :   fd_rotor_blk_t * root_blk;     /* the root's blk, NULL if not held */
     219             :   ulong            catchup_slot;
     220             :   int              reconsume;    /* replay sent a MISSING_FEC: the next FEC pushed onto reasm_deque goes with its ancestry from the root */
     221             :   ulong            slot_max;
     222             :   ulong            fec_blk_max;  /* FEC sets per blk */
     223             : 
     224             :   fd_rotor_slot_meta_t * slot_meta;   /* indexed by slot % slot_max */
     225             :   fd_rotor_blk_t *       blk_pool;
     226             :   fd_rotor_blk_map_t *   blk_map;
     227             :   fd_rotor_orphan_map_t * orphan_map;
     228             :   fd_rotor_fec_t *       fec_pool;
     229             :   fd_rotor_fec_map_t *   fec_map;
     230             :   fd_rotor_deque_t *     reasm_deque;
     231             :   fd_rotor_treap_t *     eager_treap; /* a blk is in its role's treap iff in_blk_treap */
     232             :   fd_rotor_treap_t *     notar_treap;
     233             :   fd_rotor_treap_t *     final_treap;
     234             : 
     235             :   struct {
     236             :     fd_rotor_report_fn_t report; /* called once per blk, when it completes or when it is freed incomplete, NULL disables */
     237             :     void *               ctx;
     238             :   } telemetry;
     239             : };
     240             : typedef struct fd_rotor fd_rotor_t;
     241             : 
     242             : FD_PROTOTYPES_BEGIN
     243             : 
     244             : FD_FN_CONST ulong
     245             : fd_rotor_align( void );
     246             : 
     247             : FD_FN_CONST ulong
     248             : fd_rotor_footprint( ulong slot_max,
     249             :                     ulong fec_max,
     250             :                     ulong fec_blk_max );
     251             : 
     252             : void *
     253             : fd_rotor_new( void * shmem,
     254             :               ulong  slot_max,
     255             :               ulong  fec_max,
     256             :               ulong  fec_blk_max,
     257             :               ulong  seed );
     258             : 
     259             : fd_rotor_t *
     260             : fd_rotor_join( void * shrotor );
     261             : 
     262             : void *
     263             : fd_rotor_leave( fd_rotor_t const * rotor );
     264             : 
     265             : void *
     266             : fd_rotor_delete( void * shrotor );
     267             : 
     268             : void
     269             : fd_rotor_init( fd_rotor_t *           rotor,
     270             :                ulong                  root,
     271             :                fd_mr32_t const *      root_blk_mr,
     272             :                fd_rotor_report_fn_t   report,
     273             :                void *                 report_ctx );
     274             : 
     275             : void
     276             : fd_rotor_fini( fd_rotor_t * rotor );
     277             : 
     278             : /* replay_slot   REPLAY_SIG_SLOT_DEAD
     279             : 
     280             :    replay rejected the blk, prune it so it is never redelivered, and if
     281             :    it was the eager blk invalidate the slot so turbine can't rebuild it. */
     282             : 
     283             : void
     284             : fd_rotor_blk_dead( fd_rotor_t *      rotor,
     285             :                    ulong             slot,
     286             :                    fd_mr32_t const * blk_mr );
     287             : 
     288             : /* votor_out     FD_VOTOR_SIG_CERTED FINAL, FAST_FINAL
     289             :    votor_out     FD_VOTOR_SIG_QUORUM IMPLICITLY_FINALIZED
     290             : 
     291             :    frees the slot's competing blks, creates the blk if we don't have it
     292             :    yet. */
     293             : 
     294             : fd_rotor_blk_t *
     295             : fd_rotor_blk_finalized( fd_rotor_t *      rotor,
     296             :                         ulong             slot,
     297             :                         fd_mr32_t const * blk_mr,
     298             :                         long              ts );
     299             : 
     300             : /* votor_out     FD_VOTOR_SIG_CERTED NOTAR
     301             :    votor_out     FD_VOTOR_SIG_REPAIR
     302             : 
     303             :    creates a blk to repair by blk id, NULL if we have it or the slot is
     304             :    settled. */
     305             : 
     306             : fd_rotor_blk_t *
     307             : fd_rotor_blk_notarized( fd_rotor_t *      rotor,
     308             :                         ulong             slot,
     309             :                         fd_mr32_t const * blk_mr );
     310             : 
     311             : /* net_repair    ParentAndFecSetCount response
     312             : 
     313             :    after its proof verifies against the blk id.  the FEC set count is now
     314             :    trusted, creates the parent blk if we don't have it.  counts the
     315             :    response, arriving at ts, in the blk's telemetry. */
     316             : 
     317             : fd_rotor_blk_t *
     318             : fd_rotor_blk_parented( fd_rotor_t *      rotor,
     319             :                        ulong             slot,
     320             :                        fd_mr32_t const * blk_mr,
     321             :                        ulong             parent_slot,
     322             :                        fd_mr32_t const * parent_blk_mr,
     323             :                        uint              fec_set_cnt,
     324             :                        long              ts );
     325             : 
     326             : /* votor_out     FD_VOTOR_SIG_CERTED
     327             :    votor_out     FD_VOTOR_SIG_REPAIR
     328             : 
     329             :    before the cert is handled.  seeds an empty eager blk for every slot
     330             :    up to the cert as if turbine showed its first shred, assumes no
     331             :    skips, skipped slots are pruned by finality later. */
     332             : 
     333             : void
     334             : fd_rotor_slot_catchup( fd_rotor_t * rotor,
     335             :                        ulong        slot );
     336             : 
     337             : /* shred_out     SHRED_SIG_FEC_COMPLETE
     338             :    shred_out     SHRED_SIG_FEC_COMPLETE_LEADER
     339             :    shred_out     SHRED_SIG_FEC_COMPLETE_AGAIN
     340             : 
     341             :    the shred tile already inserted the FEC set into the store.  computes
     342             :    the eager blk's dmr once it has every FEC set, our leader FECs go to
     343             :    the head of the reasm deque.  {turbine,repair,recovered}_cnt
     344             :    are the FEC set's shred counts from the shred tile, added to the
     345             :    telemetry of each blk holding the FEC set on its first completion.
     346             :    {data,parity,repair}_rcvd are its received-shred masks from the
     347             :    shred tile, kept on the FEC set from its first completion. */
     348             : 
     349             : fd_rotor_fec_t *
     350             : fd_rotor_fec_complete( fd_rotor_t *       rotor,
     351             :                        fd_shred_t const * last_shred,
     352             :                        fd_mr32_t const *  fec_mr,
     353             :                        int                is_leader,
     354             :                        uint               turbine_cnt,
     355             :                        uint               repair_cnt,
     356             :                        uint               recovered_cnt,
     357             :                        uint               data_rcvd,
     358             :                        uint               parity_rcvd,
     359             :                        uint               repair_rcvd,
     360             :                        long               ts );
     361             : 
     362             : /* shred_out     SHRED_SIG_FEC_EVICTED
     363             : 
     364             :    the resolver dropped an incomplete FEC set, so ask for every shred
     365             :    again. */
     366             : 
     367             : void
     368             : fd_rotor_fec_evicted( fd_rotor_t *      rotor,
     369             :                       ulong             slot,
     370             :                       uint              fec_set_idx,
     371             :                       fd_mr20_t const * fec_mr );
     372             : 
     373             : /* net_repair    FecSetRoot response
     374             : 
     375             :    after its proof verifies against the blk id.  fec_mr is the proven
     376             :    20-byte prefix, the FEC set's shreds can be asked for by blk id now.
     377             :    counts the response, arriving at ts, in the blk's telemetry. */
     378             : 
     379             : fd_rotor_fec_t *
     380             : fd_rotor_fec_notarized( fd_rotor_t *      rotor,
     381             :                         ulong             slot,
     382             :                         fd_mr32_t const * blk_mr,
     383             :                         uint              fec_set_idx,
     384             :                         fd_mr20_t const * fec_mr,
     385             :                         long              ts );
     386             : 
     387             : /* replay_slot   REPLAY_SIG_ROOT_ADVANCED
     388             : 
     389             :    wait for replay's signal to advance root because of redelivery
     390             :    nonsense.  TODO remove this. */
     391             : 
     392             : void
     393             : fd_rotor_root_advanced( fd_rotor_t *      rotor,
     394             :                         ulong             slot,
     395             :                         fd_mr32_t const * blk_mr );
     396             : 
     397             : /* shred_out     SHRED_SIG_SRC_* data shreds
     398             : 
     399             :    special handling for idx 0 and the last shred idx.  also called by
     400             :    fd_rotor_fec_complete.  src is the FD_EVENT_BLOCK_RECEIVED_FEC_SETS_
     401             :    FINAL_SHRED_SOURCE_* the shred arrived by, it becomes the FEC set's
     402             :    final_src until the set completes, 0 leaves final_src alone (eg. a
     403             :    reconstructed shred). */
     404             : 
     405             : void
     406             : fd_rotor_shred_insert( fd_rotor_t *       rotor,
     407             :                        fd_shred_t const * shred,
     408             :                        fd_mr32_t const *  fec_mr,
     409             :                        int                src,
     410             :                        long               ts );
     411             : 
     412             : /* shred_out     SHRED_SIG_RESULT_EQVOC
     413             : 
     414             :    also called internally on a bad block header or conflicting
     415             :    parents.  the eager blk is never repaired again, the slot is only
     416             :    reachable by blk id.  reason is the eager blk's block_received
     417             :    cancelled_reason, ts its cancelled_time. */
     418             : 
     419             : void
     420             : fd_rotor_slot_invalidated( fd_rotor_t * rotor,
     421             :                            ulong        slot,
     422             :                            int          reason,
     423             :                            long         ts );
     424             : 
     425             : /* slot must be the root or in the window after it, slots congruent mod
     426             :    slot_max share an entry, which is reset for slot first. */
     427             : 
     428             : fd_rotor_slot_meta_t *
     429             : fd_rotor_slot_meta( fd_rotor_t * rotor,
     430             :                     ulong        slot );
     431             : 
     432             : /* votor_out     FD_VOTOR_SIG_QUORUM IMPLICITLY_SKIPPED
     433             : 
     434             :    SKIP-FINAL, a finalized descendant declares this slot skipped.  frees
     435             :    the slot's blks, a skip cert alone is not final. */
     436             : 
     437             : void
     438             : fd_rotor_slot_skipped( fd_rotor_t * rotor,
     439             :                        ulong        slot );
     440             : 
     441             : FD_PROTOTYPES_END
     442             : 
     443             : #endif /* HEADER_fd_src_choreo_rotor_fd_rotor_h */

Generated by: LCOV version 1.14