LCOV - code coverage report
Current view: top level - disco/shred - fd_fec_resolver.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 5 7 71.4 %
Date: 2025-07-01 05:00:49 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             : #include "../../ballet/ed25519/fd_ed25519.h"
       6             : 
       7             : /* This header defines several methods for building and validating FEC
       8             :    sets from received shreds.  It's designed just for use by the shred
       9             :    tile, which is why it's in disco/shred.
      10             : 
      11             :    The primary complication in the interface comes from lifetimes.
      12             :    Buffers returned by the networking layer are typically ephemeral and
      13             :    we need to hold onto the data until we've finished the FEC set, so we
      14             :    need at least one memcpy.  Once we complete the FEC set, the result
      15             :    needs to go out on an mcache/dcache pair and on the network, which
      16             :    also have different lifetime requirements.  The FEC resolver goes out
      17             :    of its way to use memory in a way that doesn't require a second
      18             :    memcpy once the FEC set is complete.  To that end, the FEC resolver
      19             :    makes two promises:
      20             :    1. Once memory has been used for a specific FEC set, it will not be
      21             :       reused for a different FEC set until at least partial_depth other
      22             :       distinct FEC sets have been returned in the out_fec_set field of
      23             :       fd_fec_resolver_add_shred or fd_fec_resolver_force_complete.
      24             :    2. Once a FEC set is complete (specified with COMPLETES), the
      25             :       associated memory will not be reused for a different FEC set until
      26             :       at least complete_depth other distinct FEC sets have been returned
      27             :       from calls to fd_fec_resolver_add_shred or
      28             :       fd_fec_resolver_force_complete that return SHRED_COMPLETES.
      29             : 
      30             :    This is implemented using a freelist with an insertion point in the
      31             :    middle (which can also be seen as two chained freelists):
      32             :                  ------------------------
      33             :                  |   In progress FEC    |--------- if completed -
      34             :                  |    sets (<=depth)    |                       |
      35             :                  |                      |--- if spilled ----    |
      36             :                  ------------------------                  |    |
      37             :                        ^                                   |    |
      38             :                        |                                   V    |
      39             :                    --------                                |    |
      40             :                    |      |   -                            |    V
      41             :                    | Free |    \                           |    |
      42             :                    |      |     >=partial_depth            |    |
      43             :                    | FEC  |     /                          |    |
      44             :                    | sets |    |                           |    |
      45             :                    |      |   _                            |    |
      46             :                    --------                                V    |
      47             :                       ^  ^                                 |    |
      48             :                       |  |------<---------------<----------|    |
      49             :                       |                                         |
      50             :                    --------                                     |
      51             :                    |      | -                                   |
      52             :                    | Comp |  \                                  |
      53             :                    |leted |   complete_depth                    |
      54             :                    |      |  /                                  V
      55             :                    | FEC  |  |                                  |
      56             :                    | sets |  |                                  |
      57             :                    |      |  -                                  |
      58             :                    --------                                     |
      59             :                       ^                                         |
      60             :                       |-------------------<----------------------
      61             : 
      62             :    When a shred arrives for a new FEC set, we pull an entry from the
      63             :    head of the free FEC set queue.  If that would result in more than
      64             :    depth sets in progress, we spill the oldest in progress set, and
      65             :    insert it at the tail of the free set queue.  When we complete a set,
      66             :    we remove it from the in progress set, add it to the tail of the
      67             :    completed FEC set queue, and move one element from the head of the
      68             :    completed queue to the tail of the free queue.
      69             : 
      70             :    Since the completed queue only advances when an FEC set is completed,
      71             :    any complete FEC set stays in that queue for at least complete_depth
      72             :    completions.
      73             : 
      74             :    It might seems like this system is overkill and one combined longer
      75             :    freelist would suffice, but that's incorrect. Consider the following
      76             :    scenario: Suppose we've just completed FEC set A, so we return it
      77             :    with COMPLETES and then we put it at the end of the free list.  Now
      78             :    we receive a shred for a new FEC set, so we take memory from the head
      79             :    of the free list.  That happens many times, but we never receive
      80             :    enough shreds for any FEC set to complete one.  Eventually, we churn
      81             :    through the whole singular freelist with spilling until the memory we
      82             :    used for A gets to the head of the freelist.  Finally we receive
      83             :    enough shreds to complete the FEC set, so we return A with COMPLETES.
      84             :    Thus, from the perspective of a consumer that only cares about
      85             :    completed FEC sets, we've returned the same piece of memory twice in
      86             :    a row. */
      87             : 
      88             : /* Forward declare opaque handle.  It has a lot of types we don't
      89             :    necessarily want to bring into the includer */
      90           0 : #define FD_FEC_RESOLVER_ALIGN (128UL)
      91             : struct fd_fec_resolver;
      92             : typedef struct fd_fec_resolver fd_fec_resolver_t;
      93             : 
      94         630 : #define SHRED_CNT_NOT_SET      (UINT_MAX/2U)
      95             : 
      96             : /* fd_fec_resolver_sign_fn: used to sign shreds that require a
      97             :    retransmitter signature. */
      98             : typedef void (fd_fec_resolver_sign_fn)( void * ctx, uchar * sig, uchar const * merkle_root );
      99             : 
     100             : FD_PROTOTYPES_BEGIN
     101             : /* fd_fec_resolver_footprint returns the required footprint (in bytes as
     102             :    always) required to create an FEC set resolver that can keep track of
     103             :    `depth` in progress FEC sets, will not reuse FEC sets for at least
     104             :    partial_depth shreds or for at least complete_depth complete FEC sets
     105             :    (see above for more information).  Additionally, the FEC resolver
     106             :    remembers done_depth FEC sets to recognize duplicates vs. new FEC
     107             :    sets.  All depths must positive.
     108             : 
     109             :    fd_fec_resolver_alignment returns the required alignment of a region
     110             :    of memory for it to be used as a FEC resolver. */
     111             : FD_FN_PURE ulong fd_fec_resolver_footprint( ulong depth, ulong partial_depth, ulong complete_depth, ulong done_depth );
     112             : FD_FN_CONST ulong fd_fec_resolver_align    ( void );
     113             : 
     114             : /* fd_fec_resolver_new formats a region of memory as a FEC resolver.
     115             :    shmem must have the required alignment and footprint.  signer is a
     116             :    function pointer used to sign any shreds that require a retransmitter
     117             :    signature, and sign_ctx is an opaque pointer passed as the first
     118             :    argument to the function.  It is okay to pass NULL for signer, in
     119             :    which case, retransmission signatures will just be zeroed and
     120             :    sign_ctx will be ignored. depth, partial_depth, complete_depth, and
     121             :    done_depth are as defined above and must be positive.  sets is a
     122             :    pointer to the first of depth+partial_depth+complete_depth FEC sets
     123             :    that this resolver will take ownership of.  The FEC resolver retains
     124             :    a write interest in these FEC sets and the shreds they point to until
     125             :    the resolver is deleted.  These FEC sets and the memory for the
     126             :    shreds they point to are the only values that will be returned in the
     127             :    output parameters of _{add_shred, force_completes}.  The FEC resolver
     128             :    will reject any shreds with a shred version that does not match the
     129             :    value provided for expected_shred_version.  Shred versions are always
     130             :    non-zero, so expected_shred_version must be non-zero.  The FEC
     131             :    resolver will also reject any shred that seems to be part of a block
     132             :    containing more than max_shred_idx data or parity shreds.  Since
     133             :    shred_idx is a uint, it doesn't really make sense to have
     134             :    max_shred_idx > UINT_MAX, and max_shred_idx==0 rejects all shreds.
     135             :    Returns shmem on success and NULL on failure (logs details). */
     136             : void *
     137             : fd_fec_resolver_new( void                    * shmem,
     138             :                      fd_fec_resolver_sign_fn * signer,
     139             :                      void                    * sign_ctx,
     140             :                      ulong                     depth,
     141             :                      ulong                     partial_depth,
     142             :                      ulong                     complete_depth,
     143             :                      ulong                     done_depth,
     144             :                      fd_fec_set_t            * sets,
     145             :                      ushort                    expected_shred_version,
     146             :                      ulong                     max_shred_idx );
     147             : 
     148             : fd_fec_resolver_t * fd_fec_resolver_join( void * shmem );
     149             : 
     150          39 : #define FD_FEC_RESOLVER_SHRED_REJECTED  (-2)
     151        1197 : #define FD_FEC_RESOLVER_SHRED_IGNORED   (-1)
     152        8568 : #define FD_FEC_RESOLVER_SHRED_OKAY      ( 0)
     153         273 : #define FD_FEC_RESOLVER_SHRED_COMPLETES ( 1)
     154             : 
     155             : /* Return values + RETVAL_OFF are in [0, RETVAL_CNT) */
     156             : #define FD_FEC_RESOLVER_ADD_SHRED_RETVAL_CNT 4
     157           0 : #define FD_FEC_RESOLVER_ADD_SHRED_RETVAL_OFF 2
     158             : 
     159             : 
     160             : 
     161             : /* fd_fec_resolver_add_shred notifies the FEC resolver of a newly
     162             :    received shred.  The FEC resolver validates the shred and copies it
     163             :    into its own storage.  resolver is a local join of an FEC resolver.
     164             :    shred is a pointer to the new shred that should be added.  shred_sz
     165             :    is the size of the shred in bytes.
     166             : 
     167             :    On success ie. SHRED_{OKAY,COMPLETES}, a pointer to the fd_fec_set_t
     168             :    structure representing the FEC set of which the shred is a part will
     169             :    be written to out_fec_set.  Additionally, on success a pointer to a
     170             :    copy of shred will be written to the location pointed to by
     171             :    out_shred.  See the long explanation above about the lifetimes of
     172             :    these pointers.  Finally, on success the merkle root of the shred
     173             :    (reconstructed from the merkle proof) will be written to
     174             :    out_merkle_root.  Unlike out_{fec_set,shred}, caller owns and
     175             :    provides the memory for out_merkle_root.  If the out_merkle_root
     176             :    pointer is NULL, the argument will be ignored and merkle root will
     177             :    not be written.
     178             : 
     179             :    If the shred fails validation for any reason, returns SHRED_REJECTED
     180             :    and does not write to out_{fec_set,shred,merkle_root}.  If the shred
     181             :    is a duplicate of a shred that has already been received (ie. a shred
     182             :    with the same index but a different payload), returns SHRED_IGNORED
     183             :    does not write to out_{fec_set,shred,merkle_root}.
     184             : 
     185             :    Note that only light validation is performed on a duplicate shred, so
     186             :    a shred that is actually invalid but looks like a duplicate of a
     187             :    previously received valid shred may be considered SHRED_IGNORED
     188             :    instead of SHRED_REJECTED.
     189             : 
     190             :    This function returns SHRED_COMPLETES when the received shred is the
     191             :    last one and completes the FEC set.  In this case, the function
     192             :    populates any missing shreds in the FEC set stored in out_fec_set. */
     193             : int fd_fec_resolver_add_shred( fd_fec_resolver_t    * resolver,
     194             :                                fd_shred_t const     * shred,
     195             :                                ulong                  shred_sz,
     196             :                                uchar const          * leader_pubkey,
     197             :                                fd_fec_set_t const * * out_fec_set,
     198             :                                fd_shred_t const   * * out_shred,
     199             :                                fd_bmtree_node_t     * out_merkle_root );
     200             : 
     201             : 
     202             : /* fd_fec_resolver_done_contains returns 1 if the FEC with signature
     203             :    lives in the done_map, and thus means it has been completed. Returns
     204             :    0 otherwise. */
     205             : int
     206             : fd_fec_resolver_done_contains( fd_fec_resolver_t      * resolver,
     207             :                                fd_ed25519_sig_t const * signature );
     208             : 
     209             : /* fd_fec_resolver_shred_query returns the data shred in the FEC set
     210             :    with signature at shred_idx. The return shred is copied to the region
     211             :    of memory pointed to by out_shred, and will copy only up to
     212             :    FD_SHRED_MIN_SZ bytes.  Returns FD_FEC_RESOLVER_SHRED_REJECTED if the
     213             :    FEC set does not live in the curr_map, and in this case, the user
     214             :    should ignore the out_shred. If the FEC set with signature lives in
     215             :    the curr_map (i.e., is an in-progress FEC set), then the data shred
     216             :    at shred_idx is copied to out_shred and FD_FEC_RESOLVER_SHRED_OKAY is
     217             :    returned. Note: no validation on shred idx bounds is performed, so it
     218             :    is up to the user to ensure that provided shred_idx is between [0,
     219             :    data_cnt).  No validation that the shred at shred_idx has been
     220             :    received is performed either.  If either of these are not satisfied,
     221             :    upon return the value of out_shred[i] for i in [0, FD_SHRED_MIN_SZ)
     222             :    is undefined.
     223             : 
     224             :    The use case for this function is solely for the force completion
     225             :    API, which requires the last shred in a FEC set. This function should
     226             :    be removed at the time when force completion is removed. */
     227             : int
     228             : fd_fec_resolver_shred_query( fd_fec_resolver_t      * resolver,
     229             :                              fd_ed25519_sig_t const * signature,
     230             :                              uint                     shred_idx,
     231             :                              uchar                  * out_shred );
     232             : 
     233             : /* fd_fec_resolver_force_complete forces completion of a partial FEC set
     234             :    in the FEC resolver.
     235             : 
     236             :    API is similar to add_shred.  last_shred is what the caller has
     237             :    determined to be the last shred in the FEC set.  out_fec_set is set
     238             :    to a pointer to the complete FEC set on SHRED_COMPLETES.  Similar to
     239             :    add_shred, see the long explanation at the top of this file for
     240             :    details on the lifetime of out_fec_set.
     241             : 
     242             :    Returns SHRED_COMPLETES when last_shred validates successfully with
     243             :    the in-progress FEC set, SHRED_IGNORED if the FEC set containing
     244             :    last_shred has already been completed (done) and SHRED_REJECTED when
     245             :    the last_shred provided is obviously invalid or the in-progress FEC
     246             :    does not validate.
     247             : 
     248             :    This function is a temporary measure to address a current limitation
     249             :    in the Repair protocol where it does not support requesting coding
     250             :    shreds.  FEC resolver requires at least one coding shred to complete,
     251             :    so this function is intended to be called when the caller knows FEC
     252             :    set resolver has already received all the data shreds, but hasn't
     253             :    gotten any coding shreds, but the caller has no way to recover the
     254             :    coding shreds and make forward progress using the data shreds it does
     255             :    already have available.
     256             : 
     257             :    Note that forcing completion greatly reduces the amount of validation
     258             :    performed on the FEC set.  It only checks that the data shreds are
     259             :    consistent with one another.  If validation of the FEC set fails when
     260             :    completing (other than issues with the last shred that are obviously
     261             :    wrong eg. shred_idx > FD_REEDSOL_DATA_SHREDS_MAX), then the function,
     262             :    similar to add_shred, will dump the in-progress FEC and add it to the
     263             :    free list.  Caller should account for this ensure they only
     264             :    force_complete when they are certain last shred is the in fact the
     265             :    last shred, or the consequence is the entire FEC might be incorrectly
     266             :    discarded too early.
     267             : 
     268             :    The last_shred is used to derive the data_shred_cnt which is
     269             :    otherwise only available after receiving a coding shred.  It is an
     270             :    error to force completion of a FEC set that has already received at
     271             :    least one parity shred. */
     272             : 
     273             : int
     274             : fd_fec_resolver_force_complete( fd_fec_resolver_t *   resolver,
     275             :                                 fd_shred_t const *    last_shred,
     276             :                                 fd_fec_set_t const ** out_fec_set );
     277             : 
     278             : void * fd_fec_resolver_leave( fd_fec_resolver_t * resolver );
     279             : void * fd_fec_resolver_delete( void * shmem );
     280             : 
     281             : FD_PROTOTYPES_END
     282             : #endif /* HEADER_fd_src_disco_shred_fd_fec_resolver_h */

Generated by: LCOV version 1.14