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

          Line data    Source code
       1             : #ifndef HEADER_fd_src_discof_rotor_fd_rotor_tile_h
       2             : #define HEADER_fd_src_discof_rotor_fd_rotor_tile_h
       3             : 
       4             : #include "../../disco/tiles.h"
       5             : #include "../../disco/shred/fd_shred_tile.h"
       6             : 
       7             : /* Rotor OUTPUTS
       8             : 
       9             :    Rotor tile forwards FECs in replay order to replay tile with sig
      10             :    ROTOR_SIG_FEC_REPLAY.
      11             : 
      12             :    However since rotor is a direct drop in for repair tile, we have to make
      13             :    sure the sigs do not clobber the repair tile's sigs.
      14             : 
      15             :    See fd_repair_tile.h
      16             :    #define REPAIR_SIG_FEC_INVALID (2UL)
      17             :    #define REPAIR_SIG_FEC_LEADER  (1UL)
      18             :    #define REPAIR_SIG_FEC         (0UL)
      19             : 
      20             :    FECs are delivered in replay order. Blocks that were not repaired
      21             :    through verified (i.e., received through turbine) means are delivered
      22             :    with block_id = {0} until the last FEC in the block, in which the
      23             :    block_id is set to the computed DMR of the previously delivered FECs.
      24             : 
      25             :    Blocks that were repaired through verified means (i.e. using ag block
      26             :    id repair from a votor event) know the block_id immediately before
      27             :    the first FEC is delivered, so the block_id on the FECs of this slot
      28             :    is set to the correct value starting from fec 0. For these blocks,
      29             :    the verified bit is 1.
      30             : 
      31             :    A slot's turbine version (the eager blk, keyed {slot, 0} by replay)
      32             :    and its votor-driven versions (notar blks, keyed {slot, block_id})
      33             :    coexist, each delivered as its FECs complete.  FEC sets with the
      34             :    same merkle root are shared between versions.  When the turbine
      35             :    version completes and its DMR matches a notar blk, the notar blk is
      36             :    merged into it and freed (see dedup in fd_rotor.c).  So in time of
      37             :    network blips replay may allocate two banks for the same block; the
      38             :    notar one never completes and is eventually evicted or pruned.
      39             : 
      40             :    INPUTS: REPLAY
      41             : 
      42             :    Rotor tile consumes from replay tile the sigs
      43             :    REPLAY_SIG_ROOT_ADVANCED, REPLAY_SIG_SLOT_DEAD and
      44             :    REPLAY_SIG_MISSING_FEC.  REPLAY_SIG_SLOT_DEAD frees the dead blk; if
      45             :    it was the turbine version, turbine does not rebuild it.
      46             : 
      47             :    Pruning:
      48             : 
      49             :    Rooting is not done off of votor rooting messages, but by replay root
      50             :    advance updates.  Consider the following case:
      51             : 
      52             :    The cluster is having trouble rooting, and so we have a long chain of
      53             :    unfinalized slots. Banks begins to evict arbitrarily. It evicts slot
      54             :    N and begins executing down a different fork, but then soon after a
      55             :    finalization arrives for slot N.
      56             : 
      57             :    Replay tile updates its consensus root, but can't advance to it yet,
      58             :    because the bank for it has not been executed.  Rotor never evicts a
      59             :    blk, and thus could root from the finalized message immediately.
      60             :    This is clearly a problem; replay needs the consensus root data
      61             :    re-delivered for execution, so rotor cannot immediately prune based
      62             :    on the finalized message.
      63             : 
      64             :    Instead replay already does its own bookkeeping.  It has a highest
      65             :    known consensus root, a storage root that is the earliest slot data
      66             :    maintained, and a notified root that is the highest consensus root
      67             :    that replay verifies is live and can't be evicted.
      68             : 
      69             :    Rotor can safely assume anything below the notified root is no longer
      70             :    needed.
      71             : 
      72             :    Eviction:
      73             : 
      74             :    Replay can evict leaf banks at will, without rotor's knowledge.  This
      75             :    means rotor may continue deliverying down a lineage that banks cannot
      76             :    immediately replay, since it has evicted an ancestor.  When that
      77             :    occurs, replay should drain the rotor in-link dcache and send a
      78             :    REPLAY_SIG_MISSING_FEC to rotor.  Rotor then sends it's next FEC set
      79             :    with the full lineage starting from the rotor root.  It does this
      80             :    only for the next FEC set to deliver.  If repeated evictions occur,
      81             :    rotor can expect repeated REPLAY_SIG_MISSING_FEC messages to arrive,
      82             :    and many redundant FECs to be delivered.
      83             : 
      84             :    We assume currently that rotor will not require eviction.  The
      85             :    default size is bounded to the Agave cap on future certs it tracks.
      86             :    We can bound rotor even tighter once dynamic vote timeouts are
      87             :    implemented, and thus rotor should always have all the data replay
      88             :    needs.
      89             : 
      90             :    INPUTS: NET
      91             : 
      92             :    Alpenglow introduces three new repair types: ShredForBlockId,
      93             :    ParentAndFecCount, and FecRoot.  ShredForBlockId response type is a
      94             :    regular shred, similar to the legacy repair types, so those responses
      95             :    get routed through the shred tile and are matched by nonce for
      96             :    verification.
      97             : 
      98             :    ParentAndFecCount and FecRoot response types are metadata, not
      99             :    regular shreds. Thus the net tile routes them directly to the rotor
     100             :    tile.  The routing is done entirely by packet size, so rotor tile
     101             :    filters and validates aggressively.  The responses are matches by
     102             :    nonce and verified before being ingested by the rotor. */
     103             : 
     104             : /* keep in line with repair tile sigs */
     105             : #define REPAIR_SIG_FEC         (0UL)
     106             : #define REPAIR_SIG_FEC_LEADER  (1UL)
     107             : #define REPAIR_SIG_FEC_INVALID (2UL)
     108             : /* alpenglow type - replayable fec */
     109           0 : #define ROTOR_SIG_FEC_REPLAY  (3UL)
     110             : /* alpenglow type - completed block metadata, on rotor_rserve */
     111           0 : #define ROTOR_SIG_BLOCK       (4UL)
     112             : 
     113             : struct fd_rotor_fec_metrics {
     114             :   uint  stats_valid;        /* 1 if the counters below are populated */
     115             : 
     116             :   /* 1 if the FEC was delivered under a votor-driven version of its slot
     117             :      -- one the rotor created from a cert and filled by block id
     118             :      repair -- and 0 if it was delivered under the slot's turbine
     119             :      version, our own leader blocks included. */
     120             :   uchar votor_repaired;
     121             : 
     122             :   uint  blk_turbine_cnt;            /* block: data shreds received via turbine, plus every coding shred */
     123             :   uint  blk_repair_cnt;             /* block: data shreds received via repair */
     124             :   uint  blk_recovered_cnt;          /* block: shreds recovered via reed-solomon */
     125             :   uint  blk_data_cnt;               /* block: data shreds held */
     126             :   uint  blk_parity_cnt;             /* block: coding shreds received */
     127             :   uchar blk_slot_complete;          /* block: 1 if the end of the block is known */
     128             :   uint  blk_last_completed_fec_idx; /* block: fec_set_idx of the set that most recently completed reception, UINT_MAX if none */
     129             : 
     130             :   uint  blk_req_window_cnt;     /* block: positional specific-shred requests sent */
     131             :   uint  blk_req_highest_cnt;    /* block: highest-shred requests sent */
     132             :   uint  blk_req_orphan_cnt;     /* block: orphan requests sent */
     133             :   uint  blk_req_shred_bid_cnt;  /* block: ShredForBlockId requests sent */
     134             :   uint  blk_req_parent_cnt;     /* block: ParentAndFecCount requests sent */
     135             :   uint  blk_req_fec_root_cnt;   /* block: FecRoot requests sent */
     136             :   uint  blk_req_retransmit_cnt; /* block: requests re-issued after a timeout or bad response */
     137             :   uint  blk_repair_responses;   /* block: repair responses matched to an outstanding request */
     138             : 
     139             :   ulong blk_first_shred_ts_nanos;      /* block: when the first shred arrived, 0 if never stamped */
     140             :   ulong blk_last_shred_ts_nanos;       /* block: when the block became contiguous, 0 if it has not */
     141             :   ulong blk_first_req_ts_nanos;        /* block: when the first specific-shred repair request was sent, 0 if none */
     142             :   ulong blk_last_repair_resp_ts_nanos; /* block: when the most recent matched repair response arrived, 0 if none */
     143             : 
     144             :   long  fec_completed_ts_nanos; /* this FEC: network arrival (wallclock ns) of the shred that completed it, 0 if unavailable */
     145             : 
     146             :   /* Highest slot rotor has completed a FEC set for off the network, our
     147             :      own leader FEC sets excluded: the cluster tip.  Not a per-block
     148             :      figure like the rest of these metrics. */
     149             :   ulong highest_fec_complete_slot;
     150             : };
     151             : typedef struct fd_rotor_fec_metrics fd_rotor_fec_metrics_t;
     152             : 
     153             : struct fd_rotor_replay_fec {
     154             :    ulong     slot;
     155             :    uint      fec_set_idx;
     156             :    fd_hash_t mr;
     157             : 
     158             :    /* conditional fields */
     159             : 
     160             :    ulong     parent_slot;     /* only present if fec_set_idx is 0 or has
     161             :                                  parentUpdate. TBD, could also just have
     162             :                                  replay do parent reparsing */
     163             :    fd_hash_t parent_block_id;
     164             : 
     165             :    int       slot_complete;
     166             :    int       data_complete;
     167             :    int       is_leader;
     168             : 
     169             :    /* known_id.  This is not the same as slot_complete = 1. known_id
     170             :       should be set always to 1 if the block id was known from the
     171             :       start, i.e. these FECs were recovered through block_id repair of a
     172             :       votor event. known_id should be 0 always for blocks that were received
     173             :       through turbine.
     174             : 
     175             :       In other words, known_id is a keying instruction, not a statement about whether the block id is known:
     176             :       - known_id set: Replay keys the block by {slot, block_id} starting at FEC 0 and looks up its parent element by that key for every later FEC.
     177             :       - known_id clear: the block is a turbine version. Replay keys it by {slot, 0} until it processes the slot-complete FEC, then re-keys it to {slot, dmr}.
     178             :                         This holds for every FEC of the block, including redelivered copies, so all FECs of one block always resolve to the same element.
     179             : 
     180             :       This exists currently because replay keys incoming FECs by {slot,
     181             :       block_id} to lookup its parent element. For blocks where known_id
     182             :       is 1 (block_id) known from the beginning, it must always use the
     183             :       block_id key. Otherwise, for a turbine block whose known_id is 0,
     184             :       it should always lookup by {slot, 0}.
     185             : 
     186             :       Redelivery from root never changes known_id. It only affects
     187             :       block_id. Neither does a merge: when a turbine copy completes as
     188             :       a block whose repaired copy is also delivering, the surviving
     189             :       turbine copy keeps known_id 0, and replay keeps building its
     190             :       {slot, 0} bank, abandoning the notarized copy's bank. */
     191             :    int       known_id;
     192             :    fd_hash_t block_id; /* always populated if known_id is 1, or if slot_complete is 1.  Otherwise could be populated on redelivery or as soon as the block_id is computed.  */
     193             : 
     194             :    fd_rotor_fec_metrics_t metrics;
     195             : };
     196             : typedef struct fd_rotor_replay_fec fd_rotor_replay_fec_t;
     197             : 
     198             : /* fd_rotor_block is published to rserve when a block's slot-complete
     199             :    FEC is delivered.  merkle_roots holds the first 20B of each FEC
     200             :    root.  The frag is variable length, see FD_ROTOR_BLOCK_SZ. */
     201             : 
     202             : struct fd_rotor_block {
     203             :   ulong     slot;
     204             :   fd_hash_t block_id;
     205             :   ulong     parent_slot;
     206             :   fd_hash_t parent_block_id;
     207             :   uint      fec_set_cnt;
     208             :   uchar     merkle_roots[ FD_FEC_BLK_MAX ][ FD_SHRED_MERKLE_NODE_SZ ];
     209             : };
     210             : typedef struct fd_rotor_block fd_rotor_block_t;
     211             : 
     212           0 : #define FD_ROTOR_BLOCK_SZ(fec_set_cnt) (offsetof(fd_rotor_block_t, merkle_roots) + (ulong)(fec_set_cnt)*FD_SHRED_MERKLE_NODE_SZ)
     213             : 
     214             : #endif /* HEADER_fd_src_discof_rotor_fd_rotor_tile_h */

Generated by: LCOV version 1.14