LCOV - code coverage report
Current view: top level - flamenco/gossip/crds - fd_crds.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 3 5 60.0 %
Date: 2025-09-19 04:41:14 Functions: 0 0 -

          Line data    Source code
       1             : #ifndef HEADER_fd_src_flamenco_gossip_fd_crds_h
       2             : #define HEADER_fd_src_flamenco_gossip_fd_crds_h
       3             : 
       4             : #include "../fd_gossip_private.h"
       5             : #include "../fd_gossip_out.h"
       6             : 
       7             : #include "../../../disco/metrics/generated/fd_metrics_gossip.h"
       8             : #include "../../../ballet/sha256/fd_sha256.h"
       9             : 
      10             : struct fd_crds_entry_private;
      11             : typedef struct fd_crds_entry_private fd_crds_entry_t;
      12             : 
      13             : struct fd_crds_private;
      14             : typedef struct fd_crds_private fd_crds_t;
      15             : 
      16             : struct fd_crds_mask_iter_private;
      17             : typedef struct fd_crds_mask_iter_private fd_crds_mask_iter_t;
      18             : 
      19           9 : #define FD_CRDS_ALIGN 128UL
      20             : 
      21           3 : #define FD_CRDS_MAGIC (0xf17eda2c37c7d50UL) /* firedancer crds version 0*/
      22             : 
      23           0 : #define FD_CRDS_UPSERT_CHECK_UPSERTS ( 0)
      24           0 : #define FD_CRDS_UPSERT_CHECK_FAILS   (-1)
      25             : 
      26     2654520 : #define CRDS_MAX_CONTACT_INFO    (1<<15) /* 32768 */
      27             : 
      28             : struct fd_crds_metrics {
      29             :   ulong count[ FD_METRICS_ENUM_CRDS_VALUE_CNT ];
      30             :   ulong expired_cnt;
      31             :   ulong evicted_cnt;
      32             : 
      33             :   ulong peer_staked_cnt;
      34             :   ulong peer_unstaked_cnt;
      35             :   ulong peer_visible_stake;
      36             :   ulong peer_evicted_cnt;
      37             : 
      38             :   ulong purged_cnt;
      39             :   ulong purged_expired_cnt;
      40             :   ulong purged_evicted_cnt;
      41             : };
      42             : 
      43             : typedef struct fd_crds_metrics fd_crds_metrics_t;
      44             : 
      45             : FD_PROTOTYPES_BEGIN
      46             : 
      47             : FD_FN_CONST ulong
      48             : fd_crds_align( void );
      49             : 
      50             : FD_FN_CONST ulong
      51             : fd_crds_footprint( ulong ele_max,
      52             :                    ulong purged_max );
      53             : 
      54             : void *
      55             : fd_crds_new( void *                shmem,
      56             :              fd_rng_t *            rng,
      57             :              ulong                 ele_max,
      58             :              ulong                 purged_max,
      59             :              fd_gossip_out_ctx_t * gossip_update_out  );
      60             : 
      61             : fd_crds_t *
      62             : fd_crds_join( void * shcrds );
      63             : 
      64             : fd_crds_metrics_t const *
      65             : fd_crds_metrics( fd_crds_t const * crds );
      66             : 
      67             : /* fd_crds_advance performs housekeeping operations and should be run
      68             :    as a part of a gossip advance loop. The following operations are
      69             :    performed:
      70             :    - expire: removes stale entries from the replicated data store.
      71             :      CRDS values from staked nodes expire roughly an epoch after they
      72             :      are created, and values from non-staked nodes expire after 15
      73             :      seconds. Removed contact info entries are also published as gossip
      74             :      updates via stem.
      75             :    - re-weigh: peers are downsampled in the peer sampler if they have
      76             :      not been refreshed in <60s.
      77             : 
      78             :    There is one exception, when the node is first bootstrapping, and
      79             :    has not yet seen any staked nodes, values do not expire at all. */
      80             : 
      81             : void
      82             : fd_crds_advance( fd_crds_t *         crds,
      83             :                  long                now,
      84             :                  fd_stem_context_t * stem );
      85             : 
      86             : /* fd_crds_len returns the number of entries in the CRDS table. This
      87             :    does not include purged entries, which have a separate queue tracking
      88             :    them. See fd_crds_purged_* APIs below. */
      89             : 
      90             : ulong
      91             : fd_crds_len( fd_crds_t const * crds );
      92             : 
      93             : /* fd_crds maintains a table of purged CRDS entries.  A CRDS entry is
      94             :    purged when it is overriden by a newer form of the entry, or it is
      95             :    expired.  Such entries are no longer propagated by the node, but are
      96             :    still tracked in order to avoid re-receiving them via pull responses
      97             :    by including them in the pull request filters we generate.  This
      98             :    means we only need to hold the hash of the entry and the wallclock
      99             :    time when it was purged.
     100             : 
     101             :    Agave's gossip client maintains two such tables: one labeled "purged"
     102             :    and another "failed_inserts".  They function the same, the only
     103             :    difference lies in the conditions that trigger the insertion and the
     104             :    expiry windows.
     105             : 
     106             :     - purged, kept for 60s
     107             : 
     108             :       A CRDS value is roughly considered "purged" when it is removed
     109             :       from the gossip table due to an incoming CRDS value replacing it.
     110             : 
     111             :     - failed, kept for 20s
     112             : 
     113             :       A CRDS value is failed when it is incoming and does not upsert the
     114             :       table, or it is too old to be inserted.
     115             :    */
     116             : 
     117             : ulong
     118             : fd_crds_purged_len( fd_crds_t const * crds );
     119             : 
     120             : void
     121             : fd_crds_generate_hash( fd_sha256_t * sha,
     122             :                        uchar const * crds_value,
     123             :                        ulong         crds_value_sz,
     124             :                        uchar         out_hash[ static 32UL ] );
     125             : 
     126             : void
     127             : fd_crds_insert_failed_insert( fd_crds_t *   crds,
     128             :                               uchar const * hash,
     129             :                               long          now );
     130             : 
     131             : /* fd_crds_checks_fast checks if inserting a CRDS value would fail on
     132             :    specific conditions. Updates the CRDS purged table depending on the checks
     133             :    that failed.
     134             : 
     135             :    This isn't an exhaustive check, but that does not matter since
     136             :    fd_crds_insert will perform the full check. This avoids expensive operations
     137             :    like sigverify and hashing* if a CRDS value fails these fast checks.
     138             : 
     139             :    Returns FD_CRDS_UPSERT_CHECK_UPSERTS if the value passes the fast checks.
     140             :    Returns >0 if the value is a duplicate, with the return value denoting the
     141             :    number of duplicates seen at this point (including current). Returns
     142             :    FD_CRDS_UPSERT_CHECK_UNDETERMINED if further checks are needed
     143             :    (e.g. hash comparison). Returns FD_CRDS_UPSERT_CHECK_FAILS for other
     144             :    failures (e.g. too old). This will result in the candidate being purged.
     145             : 
     146             :    Note that this function is not idempotent as duplicate counts are tracked by
     147             :    the CRDS table.
     148             : 
     149             :    *Hashing is performed if a failure condition warrants a purge insert. */
     150             : 
     151             : int
     152             : fd_crds_checks_fast( fd_crds_t *                         crds,
     153             :                      fd_gossip_view_crds_value_t const * candidate,
     154             :                      uchar const *                       payload,
     155             :                      uchar                               from_push_msg );
     156             : 
     157             : /* fd_crds_insert inserts and indexes a CRDS value into the data store
     158             :    as a CRDS entry, so that it can be returned by future queries. This
     159             :    function should not be called if the result of fd_crds_checks_fast is
     160             :    not FD_CRDS_UPSERT_CHECK_UPSERTS.
     161             : 
     162             :    On top of inserting the CRDS entry, this function also updates the sidetable
     163             :    of ContactInfo entries and the peer samplers if the entry is a ContactInfo.
     164             :    is_from_me indicates the CRDS entry originates from this node. We exclude our
     165             :    own entries from peer samplers. origin_stake is used to weigh the peer in the
     166             :    samplers.
     167             : 
     168             :    stem is used to publish updates to {ContactInfo, Vote, LowestSlot} entries.
     169             : 
     170             :    Returns a pointer to the newly created CRDS entry. Lifetime is guaranteed
     171             :    until the next call to the following functions:
     172             :      - fd_crds_insert
     173             :      - fd_crds_expire
     174             :    Returns NULL if the insertion fails for any reason. */
     175             : 
     176             : fd_crds_entry_t const *
     177             : fd_crds_insert( fd_crds_t *                         crds,
     178             :                 fd_gossip_view_crds_value_t const * candidate_view,
     179             :                 uchar const *                       payload,
     180             :                 ulong                               origin_stake,
     181             :                 uchar                               is_from_me,
     182             :                 long                                now,
     183             :                 fd_stem_context_t *                 stem );
     184             : 
     185             : /* fd_crds_has_staked_node returns true if any node in the cluster is
     186             :    observed to have stake. This is used in timeout calculations, which
     187             :    default to an extended expiry window when we have yet to observe
     188             :    any staked peers. */
     189             : int
     190             : fd_crds_has_staked_node( fd_crds_t const * crds );
     191             : 
     192             : void
     193             : fd_crds_entry_value( fd_crds_entry_t const * entry,
     194             :                      uchar const **          value_bytes,
     195             :                      ulong *                 value_sz );
     196             : 
     197             : uchar const *
     198             : fd_crds_entry_pubkey( fd_crds_entry_t const * entry );
     199             : 
     200             : /* fd_crds_entry_hash returns a pointer to the 32b sha256 hash of the
     201             :    entry's value hash. This is used for constructing a bloom filter. */
     202             : uchar const *
     203             : fd_crds_entry_hash( fd_crds_entry_t const * entry );
     204             : 
     205             : /* fd_crds_entry_is_contact_info returns 1 if entry holds a Contact
     206             :     Info CRDS value. Assumes entry was populated with either
     207             :    fd_crds_populate_{preflight,full} */
     208             : int
     209             : fd_crds_entry_is_contact_info( fd_crds_entry_t const * entry );
     210             : 
     211             : /* fd_crds_contact_info returns a pointer to the contact info
     212             :    structure in the entry.  This is used to access the contact info
     213             :    fields in the entry, such as the pubkey, shred version, and
     214             :    socket address.
     215             : 
     216             :    Assumes crds entry is a contact info (check with
     217             :    fd_crds_entry_is_contact_info) */
     218             : fd_contact_info_t *
     219             : fd_crds_entry_contact_info( fd_crds_entry_t const * entry );
     220             : 
     221             : /* fd_crds tracks Contact Info entries with a sidetable that holds the
     222             :    fully decoded contact info of a */
     223             : 
     224             : /* fd_crds_contact_info_lookup returns a pointer to the contact info
     225             :    structure corresponding to pubkey. returns NULL if there is no such
     226             :    entry. */
     227             : 
     228             : fd_contact_info_t const *
     229             : fd_crds_contact_info_lookup( fd_crds_t const * crds,
     230             :                              uchar const *     pubkey );
     231             : 
     232             : /* fd_crds_peer_count returns the number of Contact Info entries
     233             :    present in the sidetable. The lifetime of a Contact Info entry
     234             :    tracks the lifetime of the corresponding CRDS entry. */
     235             : ulong
     236             : fd_crds_peer_count( fd_crds_t const * crds );
     237             : 
     238             : /* The CRDS table tracks whether a peer is active or not to determine
     239             :    whether it should be sampled (see sample APIs).
     240             :    fd_crds_peer_{active,inactive} provide a way to manage this state
     241             :    for a given peer.
     242             : 
     243             :    A peer's active state is typicallly determined by its ping/pong status. */
     244             : void
     245             : fd_crds_peer_active( fd_crds_t *   crds,
     246             :                      uchar const * peer_pubkey,
     247             :                      long          now );
     248             : 
     249             : void
     250             : fd_crds_peer_inactive( fd_crds_t *   crds,
     251             :                        uchar const * peer_pubkey,
     252             :                        long          now );
     253             : 
     254             : /* The CRDS Table also maintains a set of peer samplers for use in various
     255             :    Gossip tx cases. Namely
     256             :    - Rotating the active push set (bucket_samplers)
     257             :    - Selecting a pull request target (pr_sampler) */
     258             : 
     259             : 
     260             : /* fd_crds_bucket_* sample APIs are meant to be used by fd_active_set.
     261             :    Each bucket has a unique sampler. */
     262             : fd_contact_info_t const *
     263             : fd_crds_bucket_sample_and_remove( fd_crds_t * crds,
     264             :                                   fd_rng_t *  rng,
     265             :                                   ulong       bucket );
     266             : 
     267             : /* fd_crds_bucket adds back in a peer that was previously
     268             :    sampled with fd_crds_bucket_sample_and_remove.  */
     269             : void
     270             : fd_crds_bucket_add( fd_crds_t *   crds,
     271             :                     ulong         bucket,
     272             :                     uchar const * pubkey );
     273             : 
     274             : 
     275             : /* fd_crds_sample_peer randomly selects a peer node from the CRDS based
     276             :    weighted by stake.  Peers with a ContactInfo that hasn't been
     277             :    refreshed in more than 60 seconds are considered offline, and are
     278             :    downweighted in the selection by a factor of 100.  They are still
     279             :    included to mitigate eclipse attacks.  Peers with no ContactInfo in
     280             :    the CRDS are not included in the selection.  The current node is
     281             :    also excluded from the selection.  Low stake peers which are not
     282             :    active in the ping tracker, because they aren't responding to pings
     283             :    are also excluded from the sampling.  Peers with a different shred
     284             :    version than us, or with an invalid gossip socket address are also
     285             :    excluded from the sampling.
     286             : 
     287             :    If no valid peer can be found, the returned fd_contact_info_t will be
     288             :    NULL.  The caller should check for this case and handle it
     289             :    appropriately.  On success, the returned fd_contact_info_t is a
     290             :    contact info suitable for sending a gossip pull request. */
     291             : 
     292             : fd_contact_info_t const *
     293             : fd_crds_peer_sample( fd_crds_t const * crds,
     294             :                      fd_rng_t *        rng );
     295             : 
     296             : /* fd_crds_mask_iter_{init,next,done,entry} provide an API to
     297             :    iterate over the CRDS values in the table that whose hashes match
     298             :    a given mask. In the Gossip CRDS filter, the mask is applied on
     299             :    the most significant 8 bytes of the CRDS value's hash.
     300             : 
     301             :    The Gossip CRDS filter encodes the mask in two values: `mask` and
     302             :    `mask_bits`. For example, if we set `mask_bits` to 5 and 0b01010 as
     303             :    `mask`, we get the following 64-bit bitmask:
     304             :                         01010 11111111111.....
     305             : 
     306             :    Therefore, we can frame a mask match as a CRDS value's hash whose
     307             :    most significant `mask_bits` is `mask`. We can trivially define
     308             :    the range of matching hash values by setting the non-mask bits to
     309             :    all 0s or 1s to get the start and end values respectively. */
     310             : 
     311             : fd_crds_mask_iter_t *
     312             : fd_crds_mask_iter_init( fd_crds_t const * crds,
     313             :                         ulong             mask,
     314             :                         uint              mask_bits,
     315             :                         uchar             iter_mem[ static 16UL ] );
     316             : 
     317             : fd_crds_mask_iter_t *
     318             : fd_crds_mask_iter_next( fd_crds_mask_iter_t * it,
     319             :                         fd_crds_t const * crds );
     320             : 
     321             : int
     322             : fd_crds_mask_iter_done( fd_crds_mask_iter_t * it,
     323             :                         fd_crds_t const * crds );
     324             : 
     325             : fd_crds_entry_t const *
     326             : fd_crds_mask_iter_entry( fd_crds_mask_iter_t * it,
     327             :                          fd_crds_t const * crds );
     328             : 
     329             : /* fd_crds_purged_mask_iter_{init,next,done} mirrors the fd_crds_mask_*
     330             :    APIs for the purged table.  This includes purged and failed_inserts
     331             :    entries for the specified mask range.
     332             : 
     333             :    Mixing APIs (e.g., using crds init and purged next/done/hash) is UB.*/
     334             : 
     335             : fd_crds_mask_iter_t *
     336             : fd_crds_purged_mask_iter_init( fd_crds_t const * crds,
     337             :                                ulong             mask,
     338             :                                uint              mask_bits,
     339             :                                uchar             iter_mem[ static 16UL ] );
     340             : 
     341             : fd_crds_mask_iter_t *
     342             : fd_crds_purged_mask_iter_next( fd_crds_mask_iter_t * it,
     343             :                                fd_crds_t const * crds );
     344             : 
     345             : int
     346             : fd_crds_purged_mask_iter_done( fd_crds_mask_iter_t * it,
     347             :                                fd_crds_t const * crds );
     348             : 
     349             : /* fd_crds_purged_mask_iter_hash returns the hash of the current
     350             :    entry in the purged mask iterator. */
     351             : uchar const *
     352             : fd_crds_purged_mask_iter_hash( fd_crds_mask_iter_t * it,
     353             :                                fd_crds_t const * crds );
     354             : 
     355             : FD_PROTOTYPES_END
     356             : 
     357             : #endif /* HEADER_fd_src_flamenco_gossip_fd_crds_h */

Generated by: LCOV version 1.14