LCOV - code coverage report
Current view: top level - discof/forest - fd_forest.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 215 0.0 %
Date: 2026-10-23 17:13:25 Functions: 0 90 0.0 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_discof_forest_fd_forest_h
       2             : #define HEADER_fd_src_discof_forest_fd_forest_h
       3             : 
       4             : /* Forest is an API for repairing blocks as they are discovered from the
       5             :    cluster via Turbine or Gossip.  Shreds (from Turbine) and
       6             :    confirmations (from Tower) inform forest that slot exists.  Repair
       7             :    ensures that this block is received in its entirety by requesting
       8             :    repairs for missing shreds for the block.
       9             : 
      10             :    Note that forest needs to track the strict subset of shreds that are
      11             :    known by fec_resolver, store, and reasm.  If any of these structures
      12             :    have evicted shreds, forest needs to clear out the corresponding FEC
      13             :    sets from forest to be re-requested.  It's okay if shreds are evicted
      14             :    from reasm and we re-request for them and they pass through
      15             :    fec_resolver again. Although we could be creating duplicate ctxs,
      16             :    that's fine!  We might later on get an evict notice for the second
      17             :    incomplete ctx but that's okay too!!! just have a bunch of useless
      18             :    messages that eventually will get ignored when we publish past it!!!!
      19             : 
      20             :    Like other fork-aware structures, forest maintains a tree that
      21             :    records the ancestry of slots.  It also maintains references to the
      22             :    tips of each known fork (the frontier map), and also the latest
      23             :    slot we finished repairing on each fork (the consumed map).  Any slot
      24             :    that doesn't have a known ancestry connecting it back to the root yet
      25             :    is part of the orphaned map.  And the head of every orphaned tree is
      26             :    part of the subtrees map.  While this seems very verbose, it allows
      27             :    for fast iteration and lookup of the different types of slots.
      28             : 
      29             :    fd_policy makes orphan requests that recover gaps between orphaned
      30             :    subtrees and the main ancestry tree, and then the forest iterator
      31             :    suggest repairs to make progress on the tree forwards (using BFS). */
      32             : 
      33             : /* Merkle root tracking.
      34             :    For each FEC set in the slot, we record the merkle root of the first
      35             :    shred we receive in `mroots[ fec_set_idx / 32 ]`. Then for any
      36             :    shred in the same FEC inserted later, the merkle root of the new
      37             :    shred is compared to the merkle root we have stored.
      38             : 
      39             :    If they are the same  -> good.
      40             :    If they are different -> we're going to mark this merkle root as
      41             :                             incorrect. We do this by setting the merkle
      42             :                             root to a null hash for later detection.
      43             : 
      44             :   Note we don't verify the chain on each FEC arrival, because we can't
      45             :   tell whether the CMR of the following FEC is incorrect or if the
      46             :   current MR we have is incorrect.  We can only verify the chain when
      47             :   we get a confirmation of a block_id.
      48             : 
      49             :   Eventually one of two things happen:
      50             :   1. We are able to complete the version of the FEC with the merkle root
      51             :      we have stored.  This is the common case, and means we only saw one
      52             :      version of the merkle root.
      53             : 
      54             :   2. We are not able to complete any version of the FEC.
      55             :      - Imagine we get shred 0-15 of FEC_A. then get shreds 16-31 of
      56             :        FEC_B. We would have set the merkle root to the null hash for
      57             :        that FEC set, but fec_resolver would not be able to complete the
      58             :        FEC because from a shred index POV, we don't have anything we
      59             :        need to repair (and we won't be making any new requests for that
      60             :        FEC set).
      61             :        It's difficult to differentiate between a slot where we haven't
      62             :        finished repairing, and a slot we can't repair because the
      63             :        version we have is a bad version.  So merkle chaining
      64             :        verification can only be performed on slots that have all the
      65             :        shreds received.
      66             : 
      67             :   3. We receive some shreds for both FEC_A and FEC_B, but get a FEC
      68             :      completion for FEC_B.
      69             :       - Could possibly happen during turbine, like we repair some data
      70             :         shreds from FEC_A, but get a completion for FEC_B through
      71             :         turbine.  At this point we'll take whatever we have completed
      72             :         first, so overwrite our merkle root entry. It's likely being
      73             :         overwritten from the null_hash to the FEC_B merkle root.
      74             : 
      75             :   So unfortunately...because of case 2, we determine "slot completion"
      76             :   status when all the shreds in the slot have been received, NOT when
      77             :   the slot completes with all the FEC completions. We can rely on that
      78             :   at least some version of all the shreds in the slot will arrive
      79             :   eventually.
      80             : 
      81             :   As soon as we have a confirmed block id, we can verify the slot by
      82             :   verifying the chain of merkle roots backwards.  As the CMRs correctly
      83             :   chain, the verified status on each FEC set is set.  If they don't
      84             :   chain, we dump & repair that specific FEC set. For example, say the
      85             :   2nd & 3rd FEC set is incorrect. In this case, the merkle roots array
      86             :   and bitset will look like the following after one call of
      87             :   chain_verify(slot, confirmed_bid):
      88             :                                 actual last fec
      89             :                                      |
      90             :                                      v
      91             :   merkle_roots    [ A, B', C', D, E, F, confirmed_bid] <- confirmed_bid stored for convenience
      92             :   merkle_verified [ 0, 0,  0,  1, 1, 1, 1 ]
      93             : 
      94             :   At this point, C' will be dumped and repaired.  Since D is verified,
      95             :   and the CMR entry contains the correct version of C's merkle root, we
      96             :   can now verify any shred of FEC set C that arrives and reject if the
      97             :   merkle root doesn't match the cmr entry in D.
      98             : 
      99             :   After C is successfully repaired, the after_fec call in repair_tile
     100             :   will re-trigger chain_verify on the slot again.  After this call of
     101             :   chain_verify, the merkle roots array and bitset will look like this:
     102             : 
     103             :   merkle_roots    [ A, B', C, D, E, F, confirmed_bid] <- confirmed_bid stored for convenience
     104             :   merkle_verified [ 0, 0,  1, 1, 1, 1, 1 ]
     105             : 
     106             :   At this point, C is verified, but B' is detected as incorrect.  The same
     107             :   dump and repair process is repeated for B'. Once that after_fec on B
     108             :   is called, the merkle roots array and bitset will look like this:
     109             : 
     110             :   merkle_roots    [ A, B, C, D, E, F, confirmed_bid] <- confirmed_bid stored for convenience
     111             :   merkle_verified [ 1, 1,  1, 1, 1, 1, 1 ]
     112             :   confirmed = 1
     113             : 
     114             :   The chain verify progresses beyond this slot, and the ancestors of
     115             :   this slots will also be traversed until a confirmed slot is found, or
     116             :   another incorrect FEC is detected. Note that because earlier
     117             :   confirmations may have confirmed ancestors, and because there is once
     118             :   verification "in-progress at all times", confirmation status can look
     119             :   like:
     120             : 
     121             :                 slot 1 - slot 2 - slot 3 - slot 4 - slot 5 - slot 6 - slot 7 ....
     122             :   confirmed:       1       1        0        0        0       1        1
     123             : 
     124             :   i.e. there will be up to two contiguous chains of confirmed slots in
     125             :   the forest, but not more. There can be unconfirmed slots after slot 7.
     126             :   There may be forks as well, but only one fork can be confirmed.
     127             : */
     128             : 
     129             : #include "../../disco/fd_disco_base.h"
     130             : #include "../../disco/shred/fd_fec_set.h"
     131             : 
     132           0 : #define FD_FOREST_MAGIC (0xf17eda2ce7b1c0UL) /* firedancer forest version 0 */
     133             : 
     134             : /* Per-block shred idx bitsets are raw ulong words, word_cnt per block,
     135             :    living in side arrays indexed by pool idx so the per-block shred
     136             :    bound (shred_max) is a runtime value.  idx must be < shred_max. */
     137             : 
     138             : typedef ulong fd_forest_blk_idxs_t;
     139             : 
     140           0 : FD_FN_CONST static inline ulong fd_forest_blk_idxs_word_cnt( ulong shred_max ) { return (shred_max+63UL)>>6; }
     141           0 : FD_FN_PURE  static inline int   fd_forest_blk_idxs_test    ( fd_forest_blk_idxs_t const * set, ulong idx ) { return fd_ulong_extract_bit( set[ idx>>6 ], (int)(idx&63UL) ); }
     142           0 : static inline void fd_forest_blk_idxs_insert( fd_forest_blk_idxs_t * set, ulong idx ) { set[ idx>>6 ] = fd_ulong_set_bit  ( set[ idx>>6 ], (int)(idx&63UL) ); }
     143           0 : static inline void fd_forest_blk_idxs_remove( fd_forest_blk_idxs_t * set, ulong idx ) { set[ idx>>6 ] = fd_ulong_clear_bit( set[ idx>>6 ], (int)(idx&63UL) ); }
     144             : 
     145             : FD_FN_PURE static inline ulong
     146           0 : fd_forest_blk_idxs_cnt( fd_forest_blk_idxs_t const * set, ulong word_cnt ) {
     147           0 :   ulong cnt = 0UL;
     148           0 :   for( ulong i=0UL; i<word_cnt; i++ ) cnt += (ulong)fd_ulong_popcnt( set[ i ] );
     149           0 :   return cnt;
     150           0 : }
     151             : 
     152             : /* fd_forest_blk_idxs_next_unset returns the smallest idx in [lo,hi]
     153             :    whose bit is clear, or hi+1 if every bit in the range is set.  Scans
     154             :    a word at a time.  Assumes lo<=hi and hi<64*word_cnt. */
     155             : 
     156             : FD_FN_PURE static inline ulong
     157           0 : fd_forest_blk_idxs_next_unset( fd_forest_blk_idxs_t const * set, ulong lo, ulong hi ) {
     158           0 :   ulong w    = lo>>6;
     159           0 :   ulong hi_w = hi>>6;
     160           0 :   ulong bits = ~set[ w ] & (ULONG_MAX<<(lo&63UL));
     161           0 :   while( !bits && w<hi_w ) bits = ~set[ ++w ];
     162           0 :   return fd_ulong_min( (w<<6)+(ulong)fd_ulong_find_lsb_w_default( bits, 64 ), hi+1UL );
     163           0 : }
     164             : 
     165             : /* Per-FEC merkle roots, shred_max/FD_FEC_SHRED_CNT per block, also in
     166             :    a side array indexed by pool idx.  mr is initialized to null hash,
     167             :    written to when a shred is received, invalidated to invalid_mr when
     168             :    multiple versions of the merkle root are detected. */
     169             : 
     170             : struct fd_forest_mr {
     171             :   fd_hash_t mr;
     172             :   fd_hash_t cmr;
     173             : };
     174             : typedef struct fd_forest_mr fd_forest_mr_t;
     175             : 
     176             : /* Per-FEC turbine arrival stamps, shred_max/FD_FEC_SHRED_CNT per block,
     177             :    also in a side array indexed by pool idx.  first is the ms offset
     178             :    from first_shred_ts at which turbine was first observed reaching the
     179             :    set. */
     180             : 
     181             : struct fd_forest_recv {
     182             :   ushort first;
     183             : };
     184             : typedef struct fd_forest_recv fd_forest_recv_t;
     185             : 
     186             : /* fd_forest_blk_t implements a left-child, right-sibling n-ary
     187             :    tree. Each ele maintains the `pool` index of its left-most child
     188             :    (`child_idx`), its immediate-right sibling (`sibling_idx`), and its
     189             :    parent (`parent_idx`).
     190             : 
     191             :    This tree structure is gaddr-safe and supports accesses and
     192             :    operations from processes with separate local forest joins. */
     193             : 
     194             : struct __attribute__((aligned(128UL))) fd_forest_blk {
     195             :   ulong slot;        /* map key */
     196             :   ulong parent_slot; /* map key of the parent. invariant: if parent is populated, parent_slot is populated. the converse is not necessarily true. */
     197             :   ulong next;        /* internal use by fd_pool, fd_map_chain */
     198             :   ulong parent;      /* pool idx of the parent in the tree */
     199             :   ulong child;       /* pool idx of the left-child */
     200             :   ulong sibling;     /* pool idx of the right-sibling */
     201             : 
     202             :   ulong head;        /* reserved by dlist. not all blks will be part of a dlist. */
     203             :   ulong tail;        /* reserved by dlist */
     204             : 
     205             :   ulong orphan_seq; /* while an orphan subtree head: liveness token of its one live orphanq entry */
     206             : 
     207             :   uint buffered_idx; /* highest contiguous buffered shred idx */
     208             :   uint complete_idx; /* shred_idx with SLOT_COMPLETE_FLAG ie. last shred idx in the slot */
     209             : 
     210             :   /* received data shred idxs, received merkle roots and code shred idxs
     211             :      and per-FEC turbine arrival stamps are runtime-sized side arrays,
     212             :      see fd_forest_blk_{idxs,mroots,code,recv} */
     213             : 
     214             :   fd_hash_t confirmed_bid;  /* confirmed block id - can't be wrapped in the merkle roots struct because we can create sentinel blocks
     215             :                                on confirmation, and don't know the index of the last fec set until we repair the slot.
     216             :                                hash_null if unknown.  Otherwise populated by the child slot's CMR on confirmation,
     217             :                                or by a confirmation msg from tower.  Has no bearing on if the full slot is correct or not. */
     218             :   uint lowest_verified_fec; /* lowest fec index that has been verified so far, inclusive.  Equivalent to complete_idx / 32UL
     219             :                                if the last merkle root is verified, n if every merkle root after fec set n*32 is verified.
     220             :                                Otherwise, it is UINT_MAX.  If non-UINT_MAX, then confirmed_bid must be populated (but not
     221             :                                the vice versa). */
     222             : 
     223             :   uchar chain_confirmed; /* 1 if all the FECs the slot have been confirmed via fec_chain_verify, 0 otherwise.  Note confirmed_bid
     224             :                             can be populated before this is set to 1. */
     225             : 
     226             :   /* Metrics */
     227             : 
     228             :   long first_shred_ts;     /* tick of first shred rcved in slot != complete_idx */
     229             :   long last_shred_ts;      /* tick at which the slot became fully buffered (buffered_idx==complete_idx) */
     230             :   long first_req_ts;       /* tick of first request sent in slot != complete_idx */
     231             :   long last_repair_resp_ts;/* tick of the most recent repair response received for this slot */
     232             :   uint turbine_cnt;    /* number of shreds received from turbine */
     233             :   uint repair_cnt;     /* number of data shreds received from repair */
     234             :   uint recovered_cnt;  /* number of shreds recovered from reedsol recovery */
     235             : 
     236             :   uint req_window_cnt;   /* window (specific-shred) requests sent for this slot */
     237             :   uint req_highest_cnt;  /* highest-window requests sent for this slot */
     238             :   uint req_orphan_cnt;   /* orphan requests sent for this slot */
     239             :   uint req_retransmit_cnt; /* requests re-sent for this slot after a response timeout */
     240             :   uint response_cnt;     /* repair responses received for this slot */
     241             :   uchar chain_verify_failed; /* 1 if merkle chain verification flagged this block as the bad one */
     242             : };
     243             : typedef struct fd_forest_blk fd_forest_blk_t;
     244             : 
     245             : #define POOL_NAME fd_forest_pool
     246           0 : #define POOL_T    fd_forest_blk_t
     247             : #include "../../util/tmpl/fd_pool.c"
     248             : 
     249             : #define MAP_NAME  fd_forest_ancestry
     250             : #define MAP_ELE_T fd_forest_blk_t
     251           0 : #define MAP_KEY   slot
     252             : #include "../../util/tmpl/fd_map_chain.c"
     253             : 
     254             : #define MAP_NAME  fd_forest_frontier
     255             : #define MAP_ELE_T fd_forest_blk_t
     256           0 : #define MAP_KEY   slot
     257             : #include "../../util/tmpl/fd_map_chain.c"
     258             : 
     259             : #define MAP_NAME  fd_forest_orphaned
     260             : #define MAP_ELE_T fd_forest_blk_t
     261           0 : #define MAP_KEY   slot
     262             : #include "../../util/tmpl/fd_map_chain.c"
     263             : 
     264             : #define MAP_NAME  fd_forest_subtrees
     265             : #define MAP_ELE_T fd_forest_blk_t
     266           0 : #define MAP_KEY   slot
     267             : #include "../../util/tmpl/fd_map_chain.c"
     268             : 
     269             : #define DLIST_NAME  fd_forest_subtlist  /* thread a dlist through the subtree elements for fast iteration */
     270             : #define DLIST_ELE_T fd_forest_blk_t
     271           0 : #define DLIST_NEXT  head
     272           0 : #define DLIST_PREV  tail
     273             : #include "../../util/tmpl/fd_dlist.c"
     274             : 
     275             : /* Orphan subtree heads scheduled by next orphan-request deadline.
     276             :    Entries are lazy: a head pushes one entry when it is created and one
     277             :    each time it is rescheduled; nothing is removed when a head leaves.
     278             :    Every push tags the entry and the block with the next value of a
     279             :    monotonic sequence, so an entry is live iff its slot is currently a
     280             :    subtree head whose orphan_seq matches.  At most one entry per head
     281             :    can be live, hence live entries <= ele_max and a full prq
     282             :    (2*ele_max) always holds a discardable entry. */
     283             : 
     284             : struct __attribute__((aligned(32UL))) fd_forest_orphan_ent {
     285             :   long  due;  /* when the head may next be requested */
     286             :   ulong slot;
     287             :   ulong seq;  /* liveness token, live iff it matches the head's orphan_seq */
     288             : };
     289             : typedef struct fd_forest_orphan_ent fd_forest_orphan_ent_t;
     290             : 
     291             : #define PRQ_NAME    fd_forest_orphanq
     292           0 : #define PRQ_T       fd_forest_orphan_ent_t
     293           0 : #define PRQ_TIMEOUT due
     294             : #include "../../util/tmpl/fd_prq.c"
     295             : 
     296             : /* A reference to a forest element
     297             : 
     298             :    The following maps/pools are used to track future requests.
     299             : 
     300             :    Requests:
     301             :     - slots that branch from the main tree (ancestry) that are being
     302             :       repaired / have yet to be repaired.  Maintained in a dlist, where
     303             :       the head is the current slot being repaired.  Any slot in the
     304             :       requests list must be in ancestry or frontier.
     305             : 
     306             :    Orphreqs (orphaned requests):
     307             :     - slots that branch from the unconnected trees (subtrees/orphans) that are being repaired /
     308             :       have yet to be repaired.  Maintained in a dlist, where the head
     309             :       is the current orphan request being repaired.
     310             : 
     311             :       Note that orphan requests are specifically an optimization from when
     312             :       we are catching up from very far behind.  In the usual case when we
     313             :       boot and we are catching up from close behind, need orphans is
     314             :       very fast and has a non-negligible cost on total repair time.  But
     315             :       during special cases where we are catching up from very far behind,
     316             :       need orphans can take a significant time because orphan requests
     317             :       cannot be pipelined.  In this case, we can use time waiting for
     318             :       orphan requests to respond to also repair the full slots of these
     319             :       orphan trees.
     320             : 
     321             :     Consumed:
     322             :     - slots where the entire ancestry up to the root has been completed.
     323             :       There should be <= num forks elements in the consumed map.
     324             : */
     325             : struct fd_forest_ref {
     326             :   ulong idx;             /* forest pool idx of the ele this ref refers to */
     327             :   ulong next;            /* reserved by dlist */
     328             :   ulong prev;            /* reserved by dlist */
     329             :   ulong hash;            /* reserved by pool and map_chain */
     330             : };
     331             : typedef struct fd_forest_ref fd_forest_ref_t;
     332             : 
     333             : #define MAP_NAME     fd_forest_requests
     334             : #define MAP_ELE_T    fd_forest_ref_t
     335           0 : #define MAP_KEY      idx
     336           0 : #define MAP_NEXT     hash
     337             : #include "../../util/tmpl/fd_map_chain.c"
     338             : 
     339             : #define DLIST_NAME   fd_forest_reqslist
     340             : #define DLIST_ELE_T  fd_forest_ref_t
     341           0 : #define DLIST_NEXT   next
     342           0 : #define DLIST_PREV   prev
     343             : #include "../../util/tmpl/fd_dlist.c"
     344             : 
     345             : #define POOL_NAME    fd_forest_reqspool
     346           0 : #define POOL_T       fd_forest_ref_t
     347           0 : #define POOL_NEXT    hash
     348             : #include "../../util/tmpl/fd_pool.c"
     349             : 
     350             : /* Below for fast tracking of contiguous completes slots */
     351             : #define MAP_NAME     fd_forest_consumed
     352             : #define MAP_ELE_T    fd_forest_ref_t
     353           0 : #define MAP_KEY      idx
     354           0 : #define MAP_NEXT     hash
     355             : #include "../../util/tmpl/fd_map_chain.c"
     356             : 
     357             : #define DLIST_NAME   fd_forest_conslist
     358             : #define DLIST_ELE_T  fd_forest_ref_t
     359           0 : #define DLIST_NEXT   next
     360           0 : #define DLIST_PREV   prev
     361             : #include "../../util/tmpl/fd_dlist.c"
     362             : 
     363             : #define POOL_NAME    fd_forest_conspool
     364           0 : #define POOL_T       fd_forest_ref_t
     365           0 : #define POOL_NEXT    hash
     366             : #include "../../util/tmpl/fd_pool.c"
     367             : 
     368             : /* Reuse reqslist for orphan requests list, and share pool */
     369             : 
     370             : /* Internal use only for BFSing */
     371             : #define DEQUE_NAME fd_forest_deque
     372           0 : #define DEQUE_T    ulong
     373             : #include "../../util/tmpl/fd_deque_dynamic.c"
     374             : 
     375             : 
     376             : /* fd_forest_t is the top-level structure that holds the root of
     377             :    the tree, as well as the memory pools and map structures.
     378             : 
     379             :    These structures are bump-allocated and laid out contiguously in
     380             :    memory from the fd_forest_t * pointer which points to the
     381             :    beginning of the memory region.
     382             : 
     383             :    --------------------- <- fd_forest_t *
     384             :    | metadata          |
     385             :    |-------------------|
     386             :    | pool              |
     387             :    |-------------------|
     388             :    | idxs (per blk)    |
     389             :    |-------------------|
     390             :    | code (per blk)    |
     391             :    |-------------------|
     392             :    | mroots (per blk)  |
     393             :    |-------------------|
     394             :    | recv (per blk)    |
     395             :    |-------------------|
     396             :    | ancestry          |
     397             :    |-------------------|
     398             :    | frontier          |
     399             :    |-------------------|
     400             :    | subtrees          |
     401             :    |-------------------|
     402             :    | orphaned          |
     403             :    |-------------------|
     404             :    | requests          |
     405             :    |-------------------|
     406             :    | reqslist          |
     407             :    |-------------------|
     408             :    | reqspool          |
     409             :    |-------------------|
     410             :    | orphreqs          |
     411             :    |-------------------|
     412             :    | orphlist (reqlist)|
     413             :    |-------------------|
     414             :    | consumed          |
     415             :    |-------------------|
     416             :    | conspool          |
     417             :    |-------------------|
     418             :    | deque             |
     419             :    ---------------------
     420             : 
     421             :    A valid, initialized forest is always non-empty.  After
     422             :    `fd_forest_init` the forest will always have a root ele unless
     423             :    modified improperly out of forest's API.*/
     424             : 
     425             : struct fd_forest_iter {
     426             :   ulong ele_idx;
     427             :   uint  shred_idx;
     428             :   ulong list_gaddr; /* wksp gaddr of the list this iterator corresponds to */
     429             : };
     430             : typedef struct fd_forest_iter fd_forest_iter_t;
     431             : struct __attribute__((aligned(128UL))) fd_forest {
     432             :   ulong root;           /* pool idx of the root */
     433             :   ulong wksp_gaddr;     /* wksp gaddr of fd_forest in the backing wksp, non-zero gaddr */
     434             :   ulong pool_gaddr;     /* wksp gaddr of fd_pool */
     435             :   ulong shred_max;      /* max data shreds per block, bounds shred idxs and fec_set_idxs */
     436             :   ulong idxs_gaddr;     /* wksp gaddr of per-blk data shred idx bitsets, fd_forest_blk_idxs_word_cnt( shred_max ) words each */
     437             :   ulong code_gaddr;     /* wksp gaddr of per-blk code shred idx bitsets */
     438             :   ulong mroots_gaddr;   /* wksp gaddr of per-blk fd_forest_mr_t arrays, shred_max/FD_FEC_SHRED_CNT each */
     439             :   ulong recv_gaddr;     /* wksp gaddr of per-blk fd_forest_recv_t arrays, shred_max/FD_FEC_SHRED_CNT each */
     440             :   ulong ancestry_gaddr; /* wksp_gaddr of fd_forest_ancestry */
     441             :   ulong frontier_gaddr; /* leaves that needs repair */
     442             :   ulong subtrees_gaddr; /* head of orphaned trees */
     443             :   ulong orphaned_gaddr; /* map of parent_slot to singly-linked list of ele orphaned by that parent slot */
     444             : 
     445             :   ulong subtlist_gaddr; /* wksp gaddr of fd_forest_subtlist - linkedlist of subtree elements*/
     446             :   ulong orphanq_gaddr;  /* wksp gaddr of fd_forest_orphanq - orphan heads by request deadline */
     447             :   ulong orphan_seq_next; /* next orphanq entry liveness token */
     448             : 
     449             :   /* Request trackers */
     450             : 
     451             :   ulong requests_gaddr; /* map of slot to pool idx of the completed repair frontier */
     452             :   ulong reqslist_gaddr; /* wksp gaddr of fd_forest_reqslist */
     453             :   ulong reqspool_gaddr; /* wksp gaddr of fd_forest_reqspool */
     454             : 
     455             :   ulong consumed_gaddr; /* wksp gaddr of fd_forest_consumed */
     456             :   ulong conslist_gaddr; /* wksp gaddr of fd_forest_conslist */
     457             :   ulong conspool_gaddr; /* wksp gaddr of fd_forest_conspool */
     458             : 
     459             :   ulong orphreqs_gaddr; /* wksp gaddr of fd_forest_orphreqs */
     460             :   ulong orphlist_gaddr; /* wksp gaddr of fd_forest_orphlist */
     461             : 
     462             :   fd_forest_iter_t iter; /* requests iterator corresponding to head of requests deque */
     463             :   fd_forest_iter_t orphiter; /* orphan requests iterator corresponding to head of orphan requests list */
     464             : 
     465             :   ulong deque_gaddr;    /* wksp gaddr of fd_forest_deque. internal use only for BFSing */
     466             :   ulong magic;          /* ==FD_FOREST_MAGIC */
     467             : };
     468             : typedef struct fd_forest fd_forest_t;
     469             : 
     470             : FD_PROTOTYPES_BEGIN
     471             : 
     472             : /* Constructors */
     473             : 
     474             : /* fd_forest_{align,footprint} return the required alignment and
     475             :    footprint of a memory region suitable for use as forest with up to
     476             :    ele_max eles of up to shred_max data shreds each. */
     477             : 
     478             : FD_FN_CONST static inline ulong
     479           0 : fd_forest_align( void ) {
     480           0 :   return alignof(fd_forest_t);
     481           0 : }
     482             : 
     483             : FD_FN_CONST static inline ulong
     484           0 : fd_forest_footprint( ulong ele_max, ulong shred_max ) {
     485           0 :   ulong idxs_sz   = ele_max*fd_forest_blk_idxs_word_cnt( shred_max )*sizeof(fd_forest_blk_idxs_t);
     486           0 :   ulong mroots_sz = ele_max*(shred_max/FD_FEC_SHRED_CNT)*sizeof(fd_forest_mr_t);
     487           0 :   ulong recv_sz   = ele_max*(shred_max/FD_FEC_SHRED_CNT)*sizeof(fd_forest_recv_t);
     488           0 :   return FD_LAYOUT_FINI(
     489           0 :     FD_LAYOUT_APPEND(
     490           0 :     FD_LAYOUT_APPEND(
     491           0 :     FD_LAYOUT_APPEND(
     492           0 :     FD_LAYOUT_APPEND(
     493           0 :     FD_LAYOUT_APPEND(
     494           0 :     FD_LAYOUT_APPEND(
     495           0 :     FD_LAYOUT_APPEND(
     496           0 :     FD_LAYOUT_APPEND(
     497           0 :     FD_LAYOUT_APPEND(
     498           0 :     FD_LAYOUT_APPEND(
     499           0 :     FD_LAYOUT_APPEND(
     500           0 :     FD_LAYOUT_APPEND(
     501           0 :     FD_LAYOUT_APPEND(
     502           0 :     FD_LAYOUT_APPEND(
     503           0 :     FD_LAYOUT_APPEND(
     504           0 :     FD_LAYOUT_APPEND(
     505           0 :     FD_LAYOUT_APPEND(
     506           0 :     FD_LAYOUT_APPEND(
     507           0 :     FD_LAYOUT_APPEND(
     508           0 :     FD_LAYOUT_APPEND(
     509           0 :     FD_LAYOUT_APPEND(
     510           0 :     FD_LAYOUT_INIT,
     511           0 :       alignof(fd_forest_t),       sizeof(fd_forest_t)                     ),
     512           0 :       fd_forest_pool_align(),     fd_forest_pool_footprint    ( ele_max ) ),
     513           0 :       128UL,                      idxs_sz                                 ),
     514           0 :       128UL,                      idxs_sz                                 ),
     515           0 :       128UL,                      mroots_sz                               ),
     516           0 :       128UL,                      recv_sz                                 ),
     517           0 :       fd_forest_ancestry_align(), fd_forest_ancestry_footprint( ele_max ) ),
     518           0 :       fd_forest_frontier_align(), fd_forest_frontier_footprint( ele_max ) ),
     519           0 :       fd_forest_subtrees_align(), fd_forest_subtrees_footprint( ele_max ) ),
     520           0 :       fd_forest_orphaned_align(), fd_forest_orphaned_footprint( ele_max ) ),
     521           0 :       fd_forest_subtlist_align(), fd_forest_subtlist_footprint(         ) ),
     522           0 :       fd_forest_orphanq_align(),  fd_forest_orphanq_footprint ( 2UL*ele_max ) ),
     523             : 
     524           0 :       fd_forest_requests_align(), fd_forest_requests_footprint( ele_max ) ),
     525           0 :       fd_forest_reqslist_align(), fd_forest_reqslist_footprint(         ) ),
     526           0 :       fd_forest_reqspool_align(), fd_forest_reqspool_footprint( ele_max ) ),
     527           0 :       fd_forest_consumed_align(), fd_forest_consumed_footprint( ele_max ) ),
     528           0 :       fd_forest_conslist_align(), fd_forest_conslist_footprint(         ) ),
     529           0 :       fd_forest_conspool_align(), fd_forest_conspool_footprint( ele_max ) ),
     530           0 :       fd_forest_requests_align(), fd_forest_requests_footprint( ele_max ) ),
     531           0 :       fd_forest_reqslist_align(), fd_forest_reqslist_footprint(         ) ),
     532           0 :       fd_forest_deque_align(),    fd_forest_deque_footprint   ( ele_max ) ),
     533           0 :     fd_forest_align() );
     534           0 : }
     535             : 
     536             : /* fd_forest_new formats an unused memory region for use as a
     537             :    forest.  mem is a non-NULL pointer to this region in the local
     538             :    address space with the required footprint and alignment.  ele_max
     539             :    is a power of 2, shred_max a positive multiple of FD_FEC_SHRED_CNT. */
     540             : 
     541             : void *
     542             : fd_forest_new( void * shmem, ulong ele_max, ulong shred_max, ulong seed );
     543             : 
     544             : /* fd_forest_join joins the caller to the forest.  forest
     545             :    points to the first byte of the memory region backing the forest
     546             :    in the caller's address space.  Returns a pointer in the local
     547             :    address space to forest on success. */
     548             : 
     549             : fd_forest_t *
     550             : fd_forest_join( void * forest );
     551             : 
     552             : /* fd_forest_leave leaves a current local join.  Returns a pointer
     553             :    to the underlying shared memory region on success and NULL on failure
     554             :    (logs details).  Reasons for failure include forest is NULL. */
     555             : 
     556             : void *
     557             : fd_forest_leave( fd_forest_t const * forest );
     558             : 
     559             : /* fd_forest_delete unformats a memory region used as a
     560             :    forest. Assumes only the nobody is joined to the region.
     561             :    Returns a pointer to the underlying shared memory region or NULL if
     562             :    used obviously in error (e.g. forest is obviously not a
     563             :    forest ... logs details). The ownership of the memory region is
     564             :    transferred to the caller. */
     565             : 
     566             : void *
     567             : fd_forest_delete( void * forest );
     568             : 
     569             : /* fd_forest_init initializes a forest.  Assumes forest
     570             :    is a valid local join and no one else is joined.  root is the initial
     571             :    root forest will use.  This is the snapshot slot if booting from
     572             :    a snapshot, 0 if the genesis slot.
     573             : 
     574             :    In general, this should be called by the same process that formatted
     575             :    forest's memory, ie. the caller of fd_forest_new. */
     576             : 
     577             : fd_forest_t *
     578             : fd_forest_init( fd_forest_t * forest, ulong root );
     579             : 
     580             : /* Accessors */
     581             : 
     582             : /* fd_forest_wksp returns the local join to the wksp backing the
     583             :    forest.  The lifetime of the returned pointer is at least as
     584             :    long as the lifetime of the local join.  Assumes forest is a
     585             :    current local join. */
     586             : 
     587             : FD_FN_PURE static inline fd_wksp_t *
     588           0 : fd_forest_wksp( fd_forest_t const * forest ) {
     589           0 :   return (fd_wksp_t *)( ( (ulong)forest ) - forest->wksp_gaddr );
     590           0 : }
     591             : 
     592             : /* fd_forest_{pool, pool_const} returns a pointer in the caller's address
     593             :    space to forest's element pool. */
     594             : 
     595             : FD_FN_PURE static inline fd_forest_blk_t *
     596           0 : fd_forest_pool( fd_forest_t * forest ) {
     597           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->pool_gaddr );
     598           0 : }
     599             : 
     600             : FD_FN_PURE static inline fd_forest_blk_t const *
     601           0 : fd_forest_pool_const( fd_forest_t const * forest ) {
     602           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->pool_gaddr );
     603           0 : }
     604             : 
     605             : /* fd_forest_blk_{idxs,code} return blk's data / code shred idx bitset
     606             :    (fd_forest_blk_idxs_word_cnt( forest->shred_max ) words) and
     607             :    fd_forest_blk_{mroots,recv} blk's merkle roots / per-FEC turbine
     608             :    arrival stamps (forest->shred_max / FD_FEC_SHRED_CNT entries each).
     609             :    blk must be a pool element of forest. */
     610             : 
     611             : FD_FN_PURE static inline fd_forest_blk_idxs_t *
     612           0 : fd_forest_blk_idxs( fd_forest_t const * forest, fd_forest_blk_t const * blk ) {
     613           0 :   fd_forest_blk_idxs_t * idxs = fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->idxs_gaddr );
     614           0 :   return idxs + fd_forest_pool_idx( fd_forest_pool_const( forest ), blk )*fd_forest_blk_idxs_word_cnt( forest->shred_max );
     615           0 : }
     616             : 
     617             : FD_FN_PURE static inline fd_forest_blk_idxs_t *
     618           0 : fd_forest_blk_code( fd_forest_t const * forest, fd_forest_blk_t const * blk ) {
     619           0 :   fd_forest_blk_idxs_t * code = fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->code_gaddr );
     620           0 :   return code + fd_forest_pool_idx( fd_forest_pool_const( forest ), blk )*fd_forest_blk_idxs_word_cnt( forest->shred_max );
     621           0 : }
     622             : 
     623             : FD_FN_PURE static inline fd_forest_mr_t *
     624           0 : fd_forest_blk_mroots( fd_forest_t const * forest, fd_forest_blk_t const * blk ) {
     625           0 :   fd_forest_mr_t * mroots = fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->mroots_gaddr );
     626           0 :   return mroots + fd_forest_pool_idx( fd_forest_pool_const( forest ), blk )*(forest->shred_max/FD_FEC_SHRED_CNT);
     627           0 : }
     628             : 
     629             : FD_FN_PURE static inline fd_forest_recv_t *
     630           0 : fd_forest_blk_recv( fd_forest_t const * forest, fd_forest_blk_t const * blk ) {
     631           0 :   fd_forest_recv_t * recv = fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->recv_gaddr );
     632           0 :   return recv + fd_forest_pool_idx( fd_forest_pool_const( forest ), blk )*(forest->shred_max/FD_FEC_SHRED_CNT);
     633           0 : }
     634             : 
     635             : /* fd_forest_{ancestry, ancestry_const} returns a pointer in the caller's
     636             :    address space to forest's ancestry map. */
     637             : 
     638             : FD_FN_PURE static inline fd_forest_ancestry_t *
     639           0 : fd_forest_ancestry( fd_forest_t * forest ) {
     640           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->ancestry_gaddr );
     641           0 : }
     642             : 
     643             : FD_FN_PURE static inline fd_forest_ancestry_t const *
     644           0 : fd_forest_ancestry_const( fd_forest_t const * forest ) {
     645           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->ancestry_gaddr );
     646           0 : }
     647             : 
     648             : /* fd_forest_{frontier, frontier_const} returns a pointer in the caller's
     649             :    address space to forest's frontier map. */
     650             : 
     651             : FD_FN_PURE static inline fd_forest_frontier_t *
     652           0 : fd_forest_frontier( fd_forest_t * forest ) {
     653           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->frontier_gaddr );
     654           0 : }
     655             : 
     656             : FD_FN_PURE static inline fd_forest_frontier_t const *
     657           0 : fd_forest_frontier_const( fd_forest_t const * forest ) {
     658           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->frontier_gaddr );
     659           0 : }
     660             : 
     661             : /* fd_forest_{subtrees, subtrees_const} returns a pointer in the caller's
     662             :    address space to forest's subtrees map. */
     663             : 
     664             : FD_FN_PURE static inline fd_forest_subtrees_t *
     665           0 : fd_forest_subtrees( fd_forest_t * forest ) {
     666           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->subtrees_gaddr );
     667           0 : }
     668             : 
     669             : FD_FN_PURE static inline fd_forest_subtrees_t const *
     670           0 : fd_forest_subtrees_const( fd_forest_t const * forest ) {
     671           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->subtrees_gaddr );
     672           0 : }
     673             : 
     674             : /* fd_forest_{subtlist, subtlist_const} returns a pointer in the caller's
     675             :    address space to forest's subtlist. */
     676             : 
     677             : FD_FN_PURE static inline fd_forest_subtlist_t *
     678           0 : fd_forest_subtlist( fd_forest_t * forest ) {
     679           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->subtlist_gaddr );
     680           0 : }
     681             : 
     682             : FD_FN_PURE static inline fd_forest_subtlist_t const *
     683           0 : fd_forest_subtlist_const( fd_forest_t const * forest ) {
     684           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->subtlist_gaddr );
     685           0 : }
     686             : 
     687             : FD_FN_PURE static inline fd_forest_orphan_ent_t *
     688           0 : fd_forest_orphanq( fd_forest_t * forest ) {
     689           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->orphanq_gaddr );
     690           0 : }
     691             : 
     692             : FD_FN_PURE static inline fd_forest_orphan_ent_t const *
     693           0 : fd_forest_orphanq_const( fd_forest_t const * forest ) {
     694           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->orphanq_gaddr );
     695           0 : }
     696             : 
     697             : /* fd_forest_{orphaned, orphaned_const} returns a pointer in the caller's
     698             :    address space to forest's orphaned map. */
     699             : 
     700             : FD_FN_PURE static inline fd_forest_orphaned_t *
     701           0 : fd_forest_orphaned( fd_forest_t * forest ) {
     702           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->orphaned_gaddr );
     703           0 : }
     704             : 
     705             : FD_FN_PURE static inline fd_forest_orphaned_t const *
     706           0 : fd_forest_orphaned_const( fd_forest_t const * forest ) {
     707           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->orphaned_gaddr );
     708           0 : }
     709             : 
     710             : /* fd_forest_{consumed, consumed_const} returns a pointer in the caller's
     711             :    address space to forest's consumed map. */
     712             : 
     713             : FD_FN_PURE static inline fd_forest_consumed_t *
     714           0 : fd_forest_consumed( fd_forest_t * forest ) {
     715           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->consumed_gaddr );
     716           0 : }
     717             : 
     718             : FD_FN_PURE static inline fd_forest_consumed_t const *
     719           0 : fd_forest_consumed_const( fd_forest_t const * forest ) {
     720           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->consumed_gaddr );
     721           0 : }
     722             : 
     723             : /* fd_forest_{conslist, conslist_const} returns a pointer in the caller's
     724             :    address space to forest's consumed list. */
     725             : 
     726             : FD_FN_PURE static inline fd_forest_conslist_t *
     727           0 : fd_forest_conslist( fd_forest_t * forest ) {
     728           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->conslist_gaddr );
     729           0 : }
     730             : 
     731             : FD_FN_PURE static inline fd_forest_conslist_t const *
     732           0 : fd_forest_conslist_const( fd_forest_t const * forest ) {
     733           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->conslist_gaddr );
     734           0 : }
     735             : 
     736             : /* fd_forest_{conspool, conspool_const} returns a pointer in the caller's
     737             :    address space to forest's consumed pool. */
     738             : 
     739             : FD_FN_PURE static inline fd_forest_ref_t *
     740           0 : fd_forest_conspool( fd_forest_t * forest ) {
     741           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->conspool_gaddr );
     742           0 : }
     743             : 
     744             : FD_FN_PURE static inline fd_forest_ref_t const *
     745           0 : fd_forest_conspool_const( fd_forest_t const * forest ) {
     746           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->conspool_gaddr );
     747           0 : }
     748             : 
     749             : /* fd_forest_{requests, requests_const} returns a pointer in the caller's
     750             :    address space to forest's requests map. */
     751             : 
     752             : FD_FN_PURE static inline fd_forest_requests_t *
     753           0 : fd_forest_requests( fd_forest_t * forest ) {
     754           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->requests_gaddr );
     755           0 : }
     756             : 
     757             : FD_FN_PURE static inline fd_forest_requests_t const *
     758           0 : fd_forest_requests_const( fd_forest_t const * forest ) {
     759           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->requests_gaddr );
     760           0 : }
     761             : 
     762             : /* fd_forest_{reqslist, reqslist_const} returns a pointer in the caller's
     763             :    address space to forest's reqslist. */
     764             : 
     765             : FD_FN_PURE static inline fd_forest_reqslist_t *
     766           0 : fd_forest_reqslist( fd_forest_t * forest ) {
     767           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->reqslist_gaddr );
     768           0 : }
     769             : 
     770             : FD_FN_PURE static inline fd_forest_reqslist_t const *
     771           0 : fd_forest_reqslist_const( fd_forest_t const * forest ) {
     772           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->reqslist_gaddr );
     773           0 : }
     774             : 
     775             : /* fd_forest_{orphreqs, orphanreqs_const} returns a pointer in the caller's
     776             :    address space to forest's orphanreqs. */
     777             : 
     778             : FD_FN_PURE static inline fd_forest_requests_t *
     779           0 : fd_forest_orphreqs( fd_forest_t * forest ) {
     780           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->orphreqs_gaddr );
     781           0 : }
     782             : 
     783             : FD_FN_PURE static inline fd_forest_requests_t const *
     784           0 : fd_forest_orphreqs_const( fd_forest_t const * forest ) {
     785           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->orphreqs_gaddr );
     786           0 : }
     787             : 
     788             : /* fd_forest_{orphlist, orphanlist_const} returns a pointer in the caller's
     789             :    address space to forest's orphanlist. */
     790             : 
     791             : FD_FN_PURE static inline fd_forest_reqslist_t *
     792           0 : fd_forest_orphlist( fd_forest_t * forest ) {
     793           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->orphlist_gaddr );
     794           0 : }
     795             : 
     796             : FD_FN_PURE static inline fd_forest_reqslist_t const *
     797           0 : fd_forest_orphlist_const( fd_forest_t const * forest ) {
     798           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->orphlist_gaddr );
     799           0 : }
     800             : 
     801             : /* fd_forest_{reqspool, reqspool_const} returns a pointer in the caller's
     802             :    address space to forest's reqspool pool. */
     803             : 
     804             : FD_FN_PURE static inline fd_forest_ref_t *
     805           0 : fd_forest_reqspool( fd_forest_t * forest ) {
     806           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->reqspool_gaddr );
     807           0 : }
     808             : 
     809             : FD_FN_PURE static inline fd_forest_ref_t const *
     810           0 : fd_forest_reqspool_const( fd_forest_t const * forest ) {
     811           0 :   return fd_wksp_laddr_fast( fd_forest_wksp( forest ), forest->reqspool_gaddr );
     812           0 : }
     813             : 
     814             : /* fd_forest_root_slot returns forest's root slot.  Assumes
     815             :    forest is a current local join. */
     816             : 
     817             : FD_FN_PURE static inline ulong
     818           0 : fd_forest_root_slot( fd_forest_t const * forest ) {
     819           0 :   if( FD_UNLIKELY( forest->root == fd_forest_pool_idx_null( fd_forest_pool_const( forest ) ) )) return ULONG_MAX; /* uninitialized */
     820           0 :   return fd_forest_pool_ele_const( fd_forest_pool_const( forest ), forest->root )->slot;
     821           0 : }
     822             : 
     823             : fd_forest_blk_t *
     824             : fd_forest_query( fd_forest_t * forest, ulong slot );
     825             : 
     826             : /* Operations */
     827             : 
     828             : /* fd_forest_blk_insert inserts a new block into the forest.  Assumes
     829             :    slot >= forest->root.  blk_insert can also be called to create a
     830             :    sentinel block, i.e. a placeholder block that we know exists but
     831             :    don't know the parent slot of.  The caller should pass in parent_slot
     832             :    == ULONG_MAX.  In this case, the block inserted will remain an
     833             :    orphan/subtree at least until the next blk_insert is called with a
     834             :    different parent_slot, after which point blk_insert will not update
     835             :    the parent_slot again (shred inserts may still update it, see
     836             :    fd_forest_data_shred_insert).  For non-sentinel blocks, blk insert is
     837             :    idempotent, and can be called multiple times with the same slot.
     838             : 
     839             :    If the forest pool is full at the time of insertion, a block will be
     840             :    chosen for eviction (see fd_forest.c:evict for more details).  If the
     841             :    caller passes in a non-NULL evicted pointer, the evicted slot will be
     842             :    stored to the pointer.
     843             : 
     844             :    Returns the inserted (or existing) forest ele.  NULL if the forest
     845             :    pool is full and no block could be evicted. */
     846             : 
     847             : fd_forest_blk_t *
     848             : fd_forest_blk_insert( fd_forest_t * forest, ulong slot, ulong parent_slot, ulong * evicted );
     849             : 
     850           0 : #define SHRED_SRC_TURBINE   0
     851           0 : #define SHRED_SRC_REPAIR    1
     852           0 : #define SHRED_SRC_RECOVERED 2
     853             : #define SHRED_SRC_LEADER    3
     854             : 
     855             : /* fd_forest_shred_insert inserts a new shred into the forest. Assumes
     856             :    slot is already in forest, and should typically be preceded by a
     857             :    fd_forest_blk_insert. Returns the forest ele corresponding to the
     858             :    shred slot if the shred is accepted, and NULL if the shred is
     859             :    rejected.  A shred can only be rejected if slot is able to verify
     860             :    that this shred does not belong to the canonical FEC set.
     861             : 
     862             :    A possible side effect of data_shred_insert is that it may update the
     863             :    parent slot of the block IF 1) the inserted shred has a verifiably
     864             :    correct merkle root, or 2) the shred belongs in fec set 0, and no
     865             :    other merkle roots has arrived for fec set 0.
     866             : 
     867             :    Note this is different from a sentinel block parent update. A
     868             :    sentinel block will update its parent with the first parent slot it
     869             :    receives, but it can be later updated with a data_shred_insert. */
     870             : 
     871             : fd_forest_blk_t *
     872             : fd_forest_data_shred_insert( fd_forest_t * forest,
     873             :                              ulong         slot,
     874             :                              ulong         parent_slot,
     875             :                              uint          shred_idx,
     876             :                              uint          fec_set_idx,
     877             :                              int           slot_complete,
     878             :                              int           ref_tick,
     879             :                              int           src,
     880             :                              fd_hash_t *   mr,
     881             :                              fd_hash_t *   cmr,
     882             :                              long          rx_tick );
     883             : 
     884             : fd_forest_blk_t *
     885             : fd_forest_code_shred_insert( fd_forest_t * forest, ulong slot, uint shred_idx, long rx_tick );
     886             : 
     887             : /* fd_forest_fec_insert inserts a new fully completed FEC set into the
     888             :    forest. Assumes slot is already in forest, and should typically be
     889             :    called directly after fd_forest_block_insert. Returns the forest ele
     890             :    corresponding to the shred slot if the FEC was accepted, NULL
     891             :    otherwise.  Like data_shred_insert, this may update the block's
     892             :    parent slot: a completed FEC set 0 whose merkle root overwrites a
     893             :    conflicting recorded version re-links the block to the parent named
     894             :    by the completing shred. */
     895             : 
     896             : fd_forest_blk_t *
     897             : fd_forest_fec_insert( fd_forest_t * forest,
     898             :                       ulong         slot,
     899             :                       ulong         parent_slot,
     900             :                       uint          last_shred_idx,
     901             :                       uint          fec_set_idx,
     902             :                       int           slot_complete,
     903             :                       int           ref_tick,
     904             :                       fd_hash_t *   mr,
     905             :                       fd_hash_t *   cmr,
     906             :                       long          rx_tick );
     907             : 
     908             : /* fd_forest_fec_clear clears the FEC set at the given slot and
     909             :    fec_set_idx.
     910             :    Can fec_clear break requests frontier invariants? No.
     911             : 
     912             :     2) If slot n is in scope of the forest root, then the shred
     913             :        delivered to repair will trigger a data_shred_insert call
     914             :        that does nothing, as repair already has record of that
     915             :        shred.  Eventually the fec_completes or fec_clear msg will be
     916             :        delivered to repair. fec_insert will do nothing. fec_clear
     917             :        will remove the idxs for the shreds from the bitset, and
     918             :        update the buffered_idx. This doesn't matter though! because
     919             :        we already have moved past slot n on the requests frontier.
     920             :        No need to request those shreds again.
     921             : 
     922             :   Except 2) breaks a bit with in specific leader slot cases. See
     923             :   fd_forest_fec_clear for more details. */
     924             : void
     925             : fd_forest_fec_clear( fd_forest_t * forest, ulong slot, uint fec_set_idx, uint max_shred_idx );
     926             : 
     927             : /* fd_forest_fec_chain_verify verifies the chain of merkle roots for a
     928             :    given block. Should only be called on a block that has all the shreds
     929             :    received. Returns a pointer to the first slot that does not confirm,
     930             :    or NULL if the chain is valid. */
     931             : fd_forest_blk_t *
     932             : fd_forest_fec_chain_verify( fd_forest_t * forest, fd_forest_blk_t * ele, fd_hash_t const * mr );
     933             : 
     934             : void
     935             : fd_forest_confirm( fd_forest_t * forest, fd_forest_blk_t * ele, fd_hash_t const * bid );
     936             : 
     937             : /* fd_forest_merkle_last_incorrect_idx returns the highest incorrect FEC
     938             :    index for a given block. */
     939             : static inline uint
     940           0 : fd_forest_merkle_last_incorrect_idx( fd_forest_blk_t * ele ) {
     941           0 :   ulong first_verified_fec = ele->lowest_verified_fec;
     942             :   /* UNLIKELY because this is being called because we've detected an incorrect FEC */
     943           0 :   if( FD_UNLIKELY( first_verified_fec == 0 ) ) return UINT_MAX;
     944             : 
     945           0 :   uint bad_fec_idx = first_verified_fec == UINT_MAX ? ele->complete_idx / 32UL /* last FEC is wrong */
     946           0 :                                                     : (uint)first_verified_fec - 1;
     947           0 :   return bad_fec_idx * 32UL;
     948           0 : }
     949             : 
     950             : /* fd_forest_publish publishes slot as the new forest root, setting
     951             :    the subtree beginning from slot as the new forest tree (ie. slot
     952             :    and all its descendants).  Prunes all eles not in slot's forest.
     953             :    Assumes slot is present in forest.  Returns the new root. */
     954             : 
     955             : fd_forest_blk_t const *
     956             : fd_forest_publish( fd_forest_t * forest, ulong slot );
     957             : 
     958             : /* fd_forest_highest_repaired_slot returns the highest child of a fully,
     959             :    contiguously repaired slot. */
     960             : ulong
     961             : fd_forest_highest_repaired_slot( fd_forest_t const * forest );
     962             : 
     963             : /* fd_forest_iter_* takes either the standard iterator or the orphan
     964             :    iterator and returns the next shred to request.  The iterator must
     965             :    one of the two iterators that is owned by the forest.
     966             : 
     967             :    The iterator will be in an iter_done state if there are no current
     968             :    shreds to request.
     969             : 
     970             :    The forward forest iterator will visit each shred at most once over
     971             :    the lifetime of the forest, without revisiting past shreds, so it is
     972             :    up to the caller to track which shreds will need re-requesting.  The
     973             :    exception to the rule is slots where the slot_complete shred is still
     974             :    not known - the highest window idx will be requested for that slot,
     975             :    and the slot will be added to the tail of the requests deque so that
     976             :    later we may revisit it.  As a result, the children of that slot may
     977             :    also be revisited multiple times.
     978             : 
     979             :    Note this case is pretty rare.
     980             : 
     981             :    An iterator signifies to the repair tile to request the
     982             :    highest_window_index when the ele_idx is not null and shred_idx is
     983             :    UINT_MAX.
     984             : 
     985             :    Otherwise, the iterator signifies to the repair tile to request a
     986             :    regular shred window_idx.
     987             : 
     988             :    Invariants for requests map and requests deque:
     989             : 
     990             :    There can only be one occurrence of the slot in the requests deque at
     991             :    any time. Any slot in the requests deque must exist in the requests
     992             :    map, and vice versa. Any slot in the requests map must also exist in
     993             :    the forest.  During publish the requests map must also be pruned.
     994             : 
     995             :    If we are mid-request of a slot that gets pruned, forest will take
     996             :    responsibility to update the iterator to a valid slot.
     997             : 
     998             :    TODO: should this really be an iterator?? or just a _next function? */
     999             : 
    1000             : fd_forest_iter_t *
    1001             : fd_forest_iter_next( fd_forest_iter_t * iter, fd_forest_t * forest );
    1002             : 
    1003             : int
    1004             : fd_forest_iter_done( fd_forest_iter_t * iter, fd_forest_t * forest );
    1005             : 
    1006             : /* Misc */
    1007             : 
    1008             : /* fd_forest_verify checks the forest is not obviously corrupt.
    1009             :    Returns 0 if verify succeeds, -1 otherwise. */
    1010             : 
    1011             : int
    1012             : fd_forest_verify( fd_forest_t const * forest );
    1013             : 
    1014             : /* fd_forest_print pretty-prints a formatted forest tree.  Printing begins
    1015             :    from `ele` (it will appear as the root in the print output).
    1016             : 
    1017             :    The most straightforward and commonly used printing pattern is:
    1018             :    `fd_forest_print( forest, fd_forest_root( forest ) )`
    1019             : 
    1020             :    This would print forest beginning from the root.
    1021             : 
    1022             :    Alternatively, caller can print a more localized view, for example
    1023             :    starting from the grandparent of the most recently executed slot:
    1024             : 
    1025             :    ```
    1026             :    fd_forest_blk_t const * ele = fd_forest_query( slot );
    1027             :    fd_forest_print( forest, fd_forest_parent( fd_forest_parent( ele ) ) )
    1028             :    ```
    1029             : 
    1030             :    Callers should add null-checks as appropriate in actual usage. */
    1031             : 
    1032             : void
    1033             : fd_forest_print( fd_forest_t const * forest );
    1034             : 
    1035             : FD_PROTOTYPES_END
    1036             : 
    1037             : #endif /* HEADER_fd_src_discof_forest_fd_forest_h */

Generated by: LCOV version 1.14