LCOV - code coverage report
Current view: top level - disco/shred - fd_fec_resolver.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 5 6 83.3 %
Date: 2025-03-20 12:08:36 Functions: 0 0 -

          Line data    Source code
       1             : #ifndef HEADER_fd_src_disco_shred_fd_fec_resolver_h
       2             : #define HEADER_fd_src_disco_shred_fd_fec_resolver_h
       3             : #include "../../ballet/shred/fd_fec_set.h"
       4             : #include "../../ballet/bmtree/fd_bmtree.h"
       5             : 
       6             : /* This header defines several methods for building and validating FEC
       7             :    sets from received shreds.  It's designed just for use by the shred
       8             :    tile, which is why it's in disco/shred.
       9             : 
      10             :    The primary complication in the interface comes from lifetimes.
      11             :    Buffers returned by the networking layer are typically ephemeral and
      12             :    we need to hold onto the data until we've finished the FEC set, so we
      13             :    need at least one memcpy.  Once we complete the FEC set, the result
      14             :    needs to go out on an mcache/dcache pair and on the network, which
      15             :    also have different lifetime requirements.  The FEC resolver goes out
      16             :    of its way to use memory in a way that doesn't require a second
      17             :    memcpy once the FEC set is complete.  To that end, the FEC resolver
      18             :    makes two promises:
      19             :    1. Once memory has been used for a specific FEC set, it will not be
      20             :       reused for a different FEC set until at least partial_depth other
      21             :       distinct FEC sets have been returned in the out_fec_set field of
      22             :       fd_fec_resolver_add_shred.
      23             :    2. Once a FEC set is complete (specified with COMPLETES), the
      24             :       associated memory will not be reused for a different FEC set until
      25             :       at least complete_depth other distinct FEC sets have been returned
      26             :       from calls to fd_fec_resolver_add_shred that return
      27             :       SHRED_COMPLETES.
      28             : 
      29             :    This is implemented using a freelist with an insertion point in the
      30             :    middle (which can also be seen as two chained freelists):
      31             :                  ------------------------
      32             :                  |   In progress FEC    |--------- if completed -
      33             :                  |    sets (<=depth)    |                       |
      34             :                  |                      |--- if spilled ----    |
      35             :                  ------------------------                  |    |
      36             :                        ^                                   |    |
      37             :                        |                                   V    |
      38             :                    --------                                |    |
      39             :                    |      |   -                            |    V
      40             :                    | Free |    \                           |    |
      41             :                    |      |     >=partial_depth            |    |
      42             :                    | FEC  |     /                          |    |
      43             :                    | sets |    |                           |    |
      44             :                    |      |   _                            |    |
      45             :                    --------                                V    |
      46             :                       ^  ^                                 |    |
      47             :                       |  |------<---------------<----------|    |
      48             :                       |                                         |
      49             :                    --------                                     |
      50             :                    |      | -                                   |
      51             :                    | Comp |  \                                  |
      52             :                    |leted |   complete_depth                    |
      53             :                    |      |  /                                  V
      54             :                    | FEC  |  |                                  |
      55             :                    | sets |  |                                  |
      56             :                    |      |  -                                  |
      57             :                    --------                                     |
      58             :                       ^                                         |
      59             :                       |-------------------<----------------------
      60             : 
      61             :    When a shred arrives for a new FEC set, we pull an entry from the
      62             :    head of the free FEC set queue.  If that would result in more than
      63             :    depth sets in progress, we spill the oldest in progress set, and
      64             :    insert it at the tail of the free set queue.  When we complete a set,
      65             :    we remove it from the in progress set, add it to the tail of the
      66             :    completed FEC set queue, and move one element from the head of the
      67             :    completed queue to the tail of the free queue.
      68             : 
      69             :    Since the completed queue only advances when an FEC set is completed,
      70             :    any complete FEC set stays in that queue for at least complete_depth
      71             :    completions.
      72             : 
      73             :    It might seems like this system is overkill and one combined longer
      74             :    freelist would suffice, but that's incorrect. Consider the following
      75             :    scenario: Suppose we've just completed FEC set A, so we return it
      76             :    with COMPLETES and then we put it at the end of the free list.  Now
      77             :    we receive a shred for a new FEC set, so we take memory from the head
      78             :    of the free list.  That happens many times, but we never receive
      79             :    enough shreds for any FEC set to complete one.  Eventually, we churn
      80             :    through the whole singular freelist with spilling until the memory we
      81             :    used for A gets to the head of the freelist.  Finally we receive
      82             :    enough shreds to complete the FEC set, so we return A with COMPLETES.
      83             :    Thus, from the perspective of a consumer that only cares about
      84             :    completed FEC sets, we've returned the same piece of memory twice in
      85             :    a row. */
      86             : 
      87             : /* Forward declare opaque handle.  It has a lot of types we don't
      88             :    necessarily want to bring into the includer */
      89           3 : #define FD_FEC_RESOLVER_ALIGN (128UL)
      90             : struct fd_fec_resolver;
      91             : typedef struct fd_fec_resolver fd_fec_resolver_t;
      92             : 
      93             : /* fd_fec_resolver_sign_fn: used to sign shreds that require a
      94             :    retransmitter signature. */
      95             : typedef void (fd_fec_resolver_sign_fn)( void * ctx, uchar * sig, uchar const * merkle_root );
      96             : 
      97             : FD_PROTOTYPES_BEGIN
      98             : /* fd_fec_resolver_footprint returns the required footprint (in bytes as
      99             :    always) required to create an FEC set resolver that can keep track of
     100             :    `depth` in progress FEC sets, will not reuse FEC sets for at least
     101             :    partial_depth shreds or for at least complete_depth complete FEC sets
     102             :    (see above for more information).  Additionally, the FEC resolver
     103             :    remembers done_depth FEC sets to recognize duplicates vs. new FEC
     104             :    sets.  All depths must positive.
     105             : 
     106             :    fd_fec_resolver_alignment returns the required alignment of a region
     107             :    of memory for it to be used as a FEC resolver. */
     108             : FD_FN_PURE ulong fd_fec_resolver_footprint( ulong depth, ulong partial_depth, ulong complete_depth, ulong done_depth );
     109             : FD_FN_CONST ulong fd_fec_resolver_align    ( void );
     110             : 
     111             : /* fd_fec_resolver_new formats a region of memory as a FEC resolver.
     112             :    shmem must have the required alignment and footprint.  signer is a
     113             :    function pointer used to sign any shreds that require a retransmitter
     114             :    signature, and sign_ctx is an opaque pointer passed as the first
     115             :    argument to the function.  It is okay to pass NULL for signer, in
     116             :    which case, retransmission signatures will just be zeroed and
     117             :    sign_ctx will be ignored. depth, partial_depth, complete_depth, and
     118             :    done_depth are as defined above and must be positive.  sets is a
     119             :    pointer to the first of depth+partial_depth+complete_depth FEC sets
     120             :    that this resolver will take ownership of.  The FEC resolver retains
     121             :    a write interest in these FEC sets and the shreds they point to until
     122             :    the resolver is deleted.  These FEC sets and the memory for the
     123             :    shreds they point to are the only values that will be returned in the
     124             :    output parameters of _add_shred.  The FEC resolver will reject any
     125             :    shreds with a shred version that does not match the value provided
     126             :    for expected_shred_version.  Shred versions are always non-zero, so
     127             :    expected_shred_version must be non-zero.  The FEC resolver will also
     128             :    reject any shred that seems to be part of a block containing more
     129             :    than max_shred_idx data or parity shreds.  Since shred_idx is a uint,
     130             :    it doesn't really make sense to have max_shred_idx > UINT_MAX, and
     131             :    max_shred_idx==0 rejects all shreds.  Returns shmem on success and
     132             :    NULL on failure (logs details). */
     133             : void *
     134             : fd_fec_resolver_new( void                    * shmem,
     135             :                      fd_fec_resolver_sign_fn * signer,
     136             :                      void                    * sign_ctx,
     137             :                      ulong                     depth,
     138             :                      ulong                     partial_depth,
     139             :                      ulong                     complete_depth,
     140             :                      ulong                     done_depth,
     141             :                      fd_fec_set_t            * sets,
     142             :                      ushort                    expected_shred_version,
     143             :                      ulong                     max_shred_idx );
     144             : 
     145             : fd_fec_resolver_t * fd_fec_resolver_join( void * shmem );
     146             : 
     147          30 : #define FD_FEC_RESOLVER_SHRED_REJECTED  (-2)
     148        1107 : #define FD_FEC_RESOLVER_SHRED_IGNORED   (-1)
     149        2838 : #define FD_FEC_RESOLVER_SHRED_OKAY      ( 0)
     150          90 : #define FD_FEC_RESOLVER_SHRED_COMPLETES ( 1)
     151             : 
     152             : /* Return values + RETVAL_OFF are in [0, RETVAL_CNT) */
     153             : #define FD_FEC_RESOLVER_ADD_SHRED_RETVAL_CNT 4
     154           0 : #define FD_FEC_RESOLVER_ADD_SHRED_RETVAL_OFF 2
     155             : 
     156             : 
     157             : 
     158             : /* fd_fec_resolver_add_shred notifies the FEC resolver of a newly
     159             :    received shred.  The FEC resolver validates the shred and copies it
     160             :    into its own storage.  resolver is a local join of an FEC resolver.
     161             :    shred is a pointer to the new shred that should be added.  shred_sz
     162             :    is the size of the shred in bytes.
     163             : 
     164             :    On success ie. SHRED_{OKAY,COMPLETES}, a pointer to the fd_fec_set_t
     165             :    structure representing the FEC set of which the shred is a part will
     166             :    be written to out_fec_set.  Additionally, on success a pointer to a
     167             :    copy of shred will be written to the location pointed to by
     168             :    out_shred.  See the long explanation above about the lifetimes of
     169             :    these pointers.  Finally, on success the merkle root of the shred
     170             :    (reconstructed from the merkle proof) will be written to
     171             :    out_merkle_root.  Unlike out_{fec_set,shred}, caller owns and
     172             :    provides the memory for out_merkle_root.  If the out_merkle_root
     173             :    pointer is NULL, the argument will be ignored and merkle root will
     174             :    not be written.
     175             : 
     176             :    If the shred fails validation for any reason, returns SHRED_REJECTED
     177             :    and does not write to out_{fec_set,shred,merkle_root}.  If the shred
     178             :    is a duplicate of a shred that has already been received (ie. a shred
     179             :    with the same index but a different payload), returns SHRED_IGNORED
     180             :    does not write to out_{fec_set,shred,merkle_root}.
     181             : 
     182             :    Note that only light validation is performed on a duplicate shred, so
     183             :    a shred that is actually invalid but looks like a duplicate of a
     184             :    previously received valid shred may be considered SHRED_IGNORED
     185             :    instead of SHRED_REJECTED.
     186             : 
     187             :    This function returns SHRED_COMPLETES when the received shred is the
     188             :    last one and completes the FEC set.  In this case, the function
     189             :    populates any missing shreds in the FEC set stored in out_fec_set. */
     190             : int fd_fec_resolver_add_shred( fd_fec_resolver_t    * resolver,
     191             :                                fd_shred_t const     * shred,
     192             :                                ulong                  shred_sz,
     193             :                                uchar const          * leader_pubkey,
     194             :                                fd_fec_set_t const * * out_fec_set,
     195             :                                fd_shred_t const   * * out_shred,
     196             :                                fd_bmtree_node_t     * out_merkle_root );
     197             : 
     198             : void * fd_fec_resolver_leave( fd_fec_resolver_t * resolver );
     199             : void * fd_fec_resolver_delete( void * shmem );
     200             : 
     201             : FD_PROTOTYPES_END
     202             : #endif /* HEADER_fd_src_disco_shred_fd_fec_resolver_h */

Generated by: LCOV version 1.14