LCOV - code coverage report
Current view: top level - flamenco/gossip - fd_crds.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 541 0.0 %
Date: 2026-03-19 18:19:27 Functions: 0 30 0.0 %

          Line data    Source code
       1             : #include "fd_crds.h"
       2             : 
       3             : #include "fd_active_set.h"
       4             : #include "../../ballet/sha256/fd_sha256.h"
       5             : #include "../../funk/fd_funk_base.h" /* no link dependency, only using hash */
       6             : 
       7             : #include <string.h>
       8             : 
       9             : struct fd_crds_contact_info_entry {
      10             :   fd_gossip_contact_info_t contact_info[1];
      11             :   long                     received_wallclock_nanos;
      12             : 
      13             :   fd_crds_entry_t *        crds_entry; /* Back-pointer to CRDS pool entry */
      14             : 
      15             :   /* A list of "fresh" contact info entries is maintained, holding
      16             :      entries that have been refreshed/inserted in the last 60s in
      17             :      upsertion order (oldest first).
      18             : 
      19             :      fd_crds_advance periodically checks for and removes peers from
      20             :      this list if they exceed the threshold. Peers removed in this
      21             :      loop are also re-scored in the peer sampler. This is different
      22             :      from dropping the CRDS entry entirely, which also removes the
      23             :      entry from this list. To avoid double-popping an entry we use
      24             :      in_list as a presence check prior to removing */
      25             :   struct {
      26             :     ulong prev;
      27             :     ulong next;
      28             :     uchar in_list; /* 1 if in the fresh list, 0 otherwise */
      29             :   } fresh_dlist;
      30             : 
      31             :   /* Similar to fresh_dlist, but with a 15s timeout instead.
      32             :      Additionally, fresh_dlist explicilty excludes our own contact info
      33             :      while fresh_15s_dlist includes it. */
      34             :   struct {
      35             :     ulong prev;
      36             :     ulong next;
      37             :     uchar in_list; /* 1 if in the fresh list, 0 otherwise */
      38             :   } fresh_15s_dlist;
      39             : 
      40             :   /* The contact info side table has a separate size limit, so
      41             :      we maintain a separate evict list to make space for new
      42             :      entries */
      43             :   struct {
      44             :     ulong prev;
      45             :     ulong next;
      46             :   } evict_dlist;
      47             : 
      48             :   struct {
      49             :     ulong next;
      50             :   } pool;
      51             : };
      52             : 
      53             : typedef struct fd_crds_contact_info_entry fd_crds_contact_info_entry_t;
      54             : 
      55             : #define POOL_NAME  crds_contact_info_pool
      56           0 : #define POOL_T     fd_crds_contact_info_entry_t
      57           0 : #define POOL_NEXT  pool.next
      58             : #include "../../util/tmpl/fd_pool.c"
      59             : 
      60             : struct fd_crds_key {
      61             :   uchar tag;
      62             :   uchar pubkey[ 32UL ];
      63             :   union {
      64             :     uchar  vote_index;
      65             :     uchar  epoch_slots_index;
      66             :     ushort duplicate_shred_index;
      67             :   };
      68             : };
      69             : 
      70             : typedef struct fd_crds_key fd_crds_key_t;
      71             : 
      72             : /* The CRDS at a high level is just a list of all the messages we have
      73             :    received over gossip.  These are called the CRDS values.  Values
      74             :    are not arbitrary, and must conform to a strictly typed schema of
      75             :    around 10 different messages. */
      76             : 
      77             : struct fd_crds_entry_private {
      78             :   /* The core operation of the CRDS is to "upsert" a value.  Basically,
      79             :      all of the message types are keyed by the originators public key,
      80             :      and we only want to store the most recent message of each type.
      81             : 
      82             :     This key field is the key for the hash table. */
      83             :   fd_crds_key_t key;
      84             : 
      85             :   union {
      86             :     fd_crds_contact_info_entry_t * ci;
      87             :     ulong node_instance_token;
      88             :   };
      89             : 
      90             :   /* When an originator creates a CRDS message, they attach their local
      91             :      wallclock time to it.  This time is used to determine when a
      92             :      message should be upserted.  If messages have the same key, the
      93             :      newer one (as created by the originator) is used. */
      94             :   ulong wallclock;
      95             : 
      96             :   ushort value_sz;
      97             :   uchar  value_bytes[ FD_GOSSIP_VALUE_MAX_SZ ];
      98             :   uchar  value_hash[ 32UL ];
      99             : 
     100             :   ulong num_duplicates;
     101             :   ulong stake;
     102             : 
     103             :   struct {
     104             :     ulong next;
     105             :   } pool;
     106             : 
     107             :   /* The CRDS needs to perform a variety of actions on the message table
     108             :      quickly, so there are various indexes woven through them values to
     109             :      support these actions.  They are ...
     110             : 
     111             :      lookup is used to enable the core map<key, value> functionality
     112             :      described for upserts defined by value->key. */
     113             :   struct {
     114             :     ulong next;
     115             :     ulong prev;
     116             :   } lookup;
     117             : 
     118             :   /* The table has a fixed size message capacity, and supports eviction
     119             :      so insertion never fails.  If the table is full and we wish to
     120             :      insert a new value, the "lowest priority" message is evicted to
     121             :      make room.  This is accomplished with a treap sorted by stake, so
     122             :      the lowest stake message is removed. */
     123             :   struct {
     124             :     ulong parent;
     125             :     ulong left;
     126             :     ulong right;
     127             :     ulong prio;
     128             :     ulong next;
     129             :     ulong prev;
     130             :   } evict;
     131             : 
     132             :   /* Values in the table expire after a pre-determined amount of time,
     133             :      so we also keep a linked list of values sorted by creation time.
     134             :      The time used here is our nodes wallclock when we received the
     135             :      CRDS, not the originators local wallclock, which they could skew
     136             :      to cause their values to live longer.
     137             : 
     138             :      There are actually two lists that reuse the same pointers here,
     139             :      and a value will be in exactly one of the lists.  One is for staked
     140             :      nodes, which values expire after 48 hours, and one is for unstaked
     141             :      nodes, which expire after 15 seconds (or also 48hours if the node
     142             :      is configured as unstaked). */
     143             :   struct {
     144             :     long  wallclock_nanos;
     145             :     ulong prev;
     146             :     ulong next;
     147             :   } expire;
     148             : 
     149             :   /* In order to load balance pull request messages across peers, each
     150             :      message has a mask value that is mask_bits long.  The pull request
     151             :      is only concerned with CRDS entires with a hash where the first
     152             :      mask_bits of the hash match the mask value.
     153             : 
     154             :      We need to be able to quickly iterate over all CRDS table entries
     155             :      matching a given mask.  To do this, we store the first 8 bytes of
     156             :      the value_hash in a sorted treap. */
     157             :   struct {
     158             :     ulong hash_prefix; /* TODO: Remove .. just use hash_value */
     159             :     ulong parent;
     160             :     ulong left;
     161             :     ulong right;
     162             :     ulong next;
     163             :     ulong prev;
     164             :     ulong prio;
     165             :   } hash;
     166             : };
     167             : 
     168             : #define POOL_NAME   crds_pool
     169           0 : #define POOL_T      fd_crds_entry_t
     170           0 : #define POOL_NEXT   pool.next
     171             : 
     172             : #include "../../util/tmpl/fd_pool.c"
     173             : 
     174             : #define TREAP_NAME      evict_treap
     175             : #define TREAP_T         fd_crds_entry_t
     176             : #define TREAP_QUERY_T   void *                                         /* We don't use query ... */
     177             : #define TREAP_CMP(q,e)  (__extension__({ (void)(q); (void)(e); -1; })) /* which means we don't need to give a real
     178             :                                                                           implementation to cmp either */
     179           0 : #define TREAP_IDX_T     ulong
     180           0 : #define TREAP_LT(e0,e1) ((e0)->stake<(e1)->stake)
     181           0 : #define TREAP_PARENT    evict.parent
     182           0 : #define TREAP_LEFT      evict.left
     183           0 : #define TREAP_RIGHT     evict.right
     184           0 : #define TREAP_PRIO      evict.prio
     185             : #define TREAP_OPTIMIZE_ITERATION 1
     186           0 : #define TREAP_NEXT      evict.next
     187           0 : #define TREAP_PREV      evict.prev
     188             : 
     189             : #include "../../util/tmpl/fd_treap.c"
     190             : 
     191             : /* staked_expire_dlist tracks contact info crds entries inserted in the
     192             :    last 432000L*SLOT_DURATION_NANOS nanoseconds with nonzero active
     193             :    stake according to their epoch stake at the time they are inserted. */
     194             : #define DLIST_NAME      staked_expire_dlist
     195             : #define DLIST_ELE_T     fd_crds_entry_t
     196           0 : #define DLIST_PREV      expire.prev
     197           0 : #define DLIST_NEXT      expire.next
     198             : 
     199             : #include "../../util/tmpl/fd_dlist.c"
     200             : 
     201             : /* unstaked_expire_dlist tracks contact info crds entries from the last
     202             :    432000L*SLOT_DURATION_NANOS nanoseconds (or from the last 15 seconds,
     203             :    if this node is itself running as unstaked) with zero active stake
     204             :    according to their epoch stake at the time they are inserted. */
     205             : #define DLIST_NAME      unstaked_expire_dlist
     206             : #define DLIST_ELE_T     fd_crds_entry_t
     207           0 : #define DLIST_PREV      expire.prev
     208           0 : #define DLIST_NEXT      expire.next
     209             : 
     210             : #include "../../util/tmpl/fd_dlist.c"
     211             : 
     212             : /* fresh_15s_dlist tracks all contact info crds entries from the last
     213             :    15 seconds. */
     214             : #define DLIST_NAME      ci_fresh_15s_dlist
     215             : #define DLIST_ELE_T     fd_crds_contact_info_entry_t
     216           0 : #define DLIST_PREV      fresh_15s_dlist.prev
     217           0 : #define DLIST_NEXT      fresh_15s_dlist.next
     218             : #include "../../util/tmpl/fd_dlist.c"
     219             : 
     220             : /* crds_contact_info_fresh_list tracks all contact info crds entries
     221             :    from the last 60 seconds. */
     222             : #define DLIST_NAME  crds_contact_info_fresh_list
     223             : #define DLIST_ELE_T fd_crds_contact_info_entry_t
     224           0 : #define DLIST_PREV  fresh_dlist.prev
     225           0 : #define DLIST_NEXT  fresh_dlist.next
     226             : #include "../../util/tmpl/fd_dlist.c"
     227             : 
     228             : #define DLIST_NAME  crds_contact_info_evict_dlist
     229             : #define DLIST_ELE_T fd_crds_contact_info_entry_t
     230           0 : #define DLIST_PREV  evict_dlist.prev
     231           0 : #define DLIST_NEXT  evict_dlist.next
     232             : #include "../../util/tmpl/fd_dlist.c"
     233             : 
     234             : #define TREAP_NAME      hash_treap
     235             : #define TREAP_T         fd_crds_entry_t
     236             : #define TREAP_QUERY_T   ulong
     237           0 : #define TREAP_CMP(q,e)  ((q>e->hash.hash_prefix)-(q<e->hash.hash_prefix))
     238           0 : #define TREAP_IDX_T     ulong
     239             : #define TREAP_OPTIMIZE_ITERATION 1
     240           0 : #define TREAP_NEXT      hash.next
     241           0 : #define TREAP_PREV      hash.prev
     242           0 : #define TREAP_LT(e0,e1) ((e0)->hash.hash_prefix<(e1)->hash.hash_prefix)
     243           0 : #define TREAP_PARENT    hash.parent
     244           0 : #define TREAP_LEFT      hash.left
     245           0 : #define TREAP_RIGHT     hash.right
     246           0 : #define TREAP_PRIO      hash.prio
     247             : #include "../../util/tmpl/fd_treap.c"
     248             : 
     249             : static inline ulong
     250             : lookup_hash( fd_crds_key_t const * key,
     251           0 :              ulong                 seed ) {
     252           0 :   ulong hash_fn = ((ulong)key->tag)<<16;
     253           0 :   switch( key->tag ) {
     254           0 :   case FD_GOSSIP_VALUE_VOTE:
     255           0 :     hash_fn ^= key->vote_index;
     256           0 :     break;
     257           0 :   case FD_GOSSIP_VALUE_EPOCH_SLOTS:
     258           0 :     hash_fn ^= key->epoch_slots_index;
     259           0 :     break;
     260           0 :   case FD_GOSSIP_VALUE_DUPLICATE_SHRED:
     261           0 :     hash_fn ^= key->duplicate_shred_index;
     262           0 :     break;
     263           0 :   default:
     264           0 :     break;
     265           0 :   }
     266           0 :   return fd_funk_rec_key_hash1( key->pubkey, seed^hash_fn );
     267           0 : }
     268             : 
     269             : static inline int
     270             : lookup_eq( fd_crds_key_t const * key0,
     271           0 :            fd_crds_key_t const * key1 ) {
     272           0 :   if( FD_UNLIKELY( key0->tag!=key1->tag ) ) return 0;
     273           0 :   if( FD_UNLIKELY( !!memcmp( key0->pubkey, key1->pubkey, 32UL ) ) ) return 0;
     274           0 :   switch( key0->tag ) {
     275           0 :     case FD_GOSSIP_VALUE_VOTE:
     276           0 :       return key0->vote_index==key1->vote_index;
     277           0 :     case FD_GOSSIP_VALUE_EPOCH_SLOTS:
     278           0 :       return key0->epoch_slots_index==key1->epoch_slots_index;
     279           0 :     case FD_GOSSIP_VALUE_DUPLICATE_SHRED:
     280           0 :       return key0->duplicate_shred_index==key1->duplicate_shred_index;
     281           0 :     default:
     282           0 :       break;
     283           0 :   }
     284           0 :   return 1;
     285           0 : }
     286             : 
     287             : #define MAP_NAME          lookup_map
     288             : #define MAP_ELE_T         fd_crds_entry_t
     289             : #define MAP_KEY_T         fd_crds_key_t
     290           0 : #define MAP_KEY           key
     291           0 : #define MAP_IDX_T         ulong
     292           0 : #define MAP_NEXT          lookup.next
     293           0 : #define MAP_PREV          lookup.prev
     294           0 : #define MAP_KEY_HASH(k,s) (lookup_hash( k, s ))
     295           0 : #define MAP_KEY_EQ(k0,k1) (lookup_eq( k0, k1 ))
     296             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
     297             : 
     298             : #include "../../util/tmpl/fd_map_chain.c"
     299             : 
     300             : struct fd_crds_private {
     301             :   fd_gossip_out_ctx_t * gossip_update;
     302             : 
     303             :   fd_gossip_activity_update_fn activity_update_fn;
     304             :   void *                       activity_update_fn_ctx;
     305             : 
     306             :   fd_sha256_t sha256[1];
     307             : 
     308             :   int has_staked_node;
     309             : 
     310             :   fd_ip4_port_t entrypoints[ 16UL ];
     311             :   ulong         entrypoints_cnt;
     312             : 
     313             :   fd_crds_entry_t * pool;
     314             :   fd_crds_contact_info_entry_t * ci_pool;
     315             : 
     316             :   evict_treap_t *           evict_treap;
     317             :   staked_expire_dlist_t *   staked_expire_dlist;
     318             :   unstaked_expire_dlist_t * unstaked_expire_dlist;
     319             :   ci_fresh_15s_dlist_t *    ci_fresh_15s_dlist;
     320             :   hash_treap_t *            hash_treap;
     321             :   lookup_map_t *            lookup_map;
     322             : 
     323             :   fd_gossip_purged_t *      purged;
     324             : 
     325             :   crds_contact_info_fresh_list_t *  ci_fresh_dlist;
     326             :   crds_contact_info_evict_dlist_t * ci_evict_dlist;
     327             : 
     328             :   fd_gossip_wsample_t *    wsample;
     329             :   fd_active_set_t *        active_set;
     330             : 
     331             :   fd_crds_metrics_t metrics[1];
     332             : 
     333             :   ulong magic;
     334             : };
     335             : 
     336             : FD_FN_CONST ulong
     337           0 : fd_crds_align( void ) {
     338           0 :   return FD_CRDS_ALIGN;
     339           0 : }
     340             : 
     341             : FD_FN_CONST ulong
     342           0 : fd_crds_footprint( ulong ele_max ) {
     343           0 :   ulong l;
     344           0 :   l = FD_LAYOUT_INIT;
     345           0 :   l = FD_LAYOUT_APPEND( l, FD_CRDS_ALIGN,                         sizeof(fd_crds_t) );
     346           0 :   l = FD_LAYOUT_APPEND( l, crds_pool_align(),                     crds_pool_footprint( ele_max )                                 );
     347           0 :   l = FD_LAYOUT_APPEND( l, evict_treap_align(),                   evict_treap_footprint( ele_max )                               );
     348           0 :   l = FD_LAYOUT_APPEND( l, staked_expire_dlist_align(),           staked_expire_dlist_footprint()                                );
     349           0 :   l = FD_LAYOUT_APPEND( l, unstaked_expire_dlist_align(),         unstaked_expire_dlist_footprint()                              );
     350           0 :   l = FD_LAYOUT_APPEND( l, ci_fresh_15s_dlist_align(),            ci_fresh_15s_dlist_footprint()                                 );
     351           0 :   l = FD_LAYOUT_APPEND( l, hash_treap_align(),                    hash_treap_footprint( ele_max )                                );
     352           0 :   l = FD_LAYOUT_APPEND( l, lookup_map_align(),                    lookup_map_footprint( ele_max )                                );
     353           0 :   l = FD_LAYOUT_APPEND( l, crds_contact_info_pool_align(),        crds_contact_info_pool_footprint( FD_CONTACT_INFO_TABLE_SIZE ) );
     354           0 :   l = FD_LAYOUT_APPEND( l, crds_contact_info_fresh_list_align(),  crds_contact_info_fresh_list_footprint()                       );
     355           0 :   l = FD_LAYOUT_APPEND( l, crds_contact_info_evict_dlist_align(), crds_contact_info_evict_dlist_footprint()                      );
     356           0 :   return FD_LAYOUT_FINI( l, FD_CRDS_ALIGN );
     357           0 : }
     358             : 
     359             : void *
     360             : fd_crds_new( void *                       shmem,
     361             :              fd_ip4_port_t const *        entrypoints,
     362             :              ulong                        entrypoints_cnt,
     363             :              fd_gossip_wsample_t *        wsample,
     364             :              fd_active_set_t *            active_set, /* TODO: Remove .. circular dep */
     365             :              fd_rng_t *                   rng,
     366             :              ulong                        ele_max,
     367             :              fd_gossip_purged_t *         purged,
     368             :              fd_gossip_activity_update_fn activity_update_fn,
     369             :              void *                       activity_update_fn_ctx,
     370           0 :              fd_gossip_out_ctx_t *        gossip_update_out ) {
     371           0 :   if( FD_UNLIKELY( !shmem ) ) {
     372           0 :     FD_LOG_WARNING(( "NULL shmem" ));
     373           0 :     return NULL;
     374           0 :   }
     375             : 
     376           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_crds_align() ) ) ) {
     377           0 :     FD_LOG_WARNING(( "misaligned shmem" ));
     378           0 :     return NULL;
     379           0 :   }
     380             : 
     381           0 :   if( FD_UNLIKELY( !fd_ulong_is_pow2( ele_max ) ) ) {
     382           0 :     FD_LOG_WARNING(( "ele_max must be a power of 2" ));
     383           0 :     return NULL;
     384           0 :   }
     385             : 
     386           0 :   if( FD_UNLIKELY( !rng ) ) {
     387           0 :     FD_LOG_WARNING(( "NULL rng" ));
     388           0 :     return NULL;
     389           0 :   }
     390             : 
     391           0 :   if( FD_UNLIKELY( !purged ) ) {
     392           0 :     FD_LOG_WARNING(( "NULL purged" ));
     393           0 :     return NULL;
     394           0 :   }
     395             : 
     396           0 :   if( FD_UNLIKELY( !gossip_update_out ) ) {
     397           0 :     FD_LOG_WARNING(( "NULL gossip_out" ));
     398           0 :     return NULL;
     399           0 :   }
     400             : 
     401           0 :   FD_SCRATCH_ALLOC_INIT( l, shmem );
     402           0 :   fd_crds_t * crds              = FD_SCRATCH_ALLOC_APPEND( l, FD_CRDS_ALIGN,                         sizeof(fd_crds_t) );
     403           0 :   void * _pool                  = FD_SCRATCH_ALLOC_APPEND( l, crds_pool_align(),                     crds_pool_footprint( ele_max )                                 );
     404           0 :   void * _evict_treap           = FD_SCRATCH_ALLOC_APPEND( l, evict_treap_align(),                   evict_treap_footprint( ele_max )                               );
     405           0 :   void * _staked_expire_dlist   = FD_SCRATCH_ALLOC_APPEND( l, staked_expire_dlist_align(),           staked_expire_dlist_footprint()                                );
     406           0 :   void * _unstaked_expire_dlist = FD_SCRATCH_ALLOC_APPEND( l, unstaked_expire_dlist_align(),         unstaked_expire_dlist_footprint()                              );
     407           0 :   void * _ci_fresh_15s_dlist    = FD_SCRATCH_ALLOC_APPEND( l, ci_fresh_15s_dlist_align(),            ci_fresh_15s_dlist_footprint()                                 );
     408           0 :   void * _hash_treap            = FD_SCRATCH_ALLOC_APPEND( l, hash_treap_align(),                    hash_treap_footprint( ele_max )                                );
     409           0 :   void * _lookup_map            = FD_SCRATCH_ALLOC_APPEND( l, lookup_map_align(),                    lookup_map_footprint( ele_max )                                );
     410           0 :   void * _ci_pool               = FD_SCRATCH_ALLOC_APPEND( l, crds_contact_info_pool_align(),        crds_contact_info_pool_footprint( FD_CONTACT_INFO_TABLE_SIZE ) );
     411           0 :   void * _ci_dlist              = FD_SCRATCH_ALLOC_APPEND( l, crds_contact_info_fresh_list_align(),  crds_contact_info_fresh_list_footprint()                       );
     412           0 :   void * _ci_evict_dlist        = FD_SCRATCH_ALLOC_APPEND( l, crds_contact_info_evict_dlist_align(), crds_contact_info_evict_dlist_footprint()                      );
     413             : 
     414           0 :   crds->activity_update_fn = activity_update_fn;
     415           0 :   FD_TEST( crds->activity_update_fn );
     416             : 
     417           0 :   crds->activity_update_fn_ctx = activity_update_fn_ctx;
     418             : 
     419           0 :   crds->pool = crds_pool_join( crds_pool_new( _pool, ele_max ) );
     420           0 :   FD_TEST( crds->pool );
     421             : 
     422           0 :   crds->evict_treap = evict_treap_join( evict_treap_new( _evict_treap, ele_max ) );
     423           0 :   FD_TEST( crds->evict_treap );
     424           0 :   evict_treap_seed( crds->pool, ele_max, fd_rng_ulong( rng ) );
     425             : 
     426           0 :   crds->staked_expire_dlist = staked_expire_dlist_join( staked_expire_dlist_new( _staked_expire_dlist ) );
     427           0 :   FD_TEST( crds->staked_expire_dlist );
     428             : 
     429           0 :   crds->unstaked_expire_dlist = unstaked_expire_dlist_join( unstaked_expire_dlist_new( _unstaked_expire_dlist ) );
     430           0 :   FD_TEST( crds->unstaked_expire_dlist );
     431             : 
     432           0 :   crds->ci_fresh_15s_dlist = ci_fresh_15s_dlist_join( ci_fresh_15s_dlist_new( _ci_fresh_15s_dlist ) );
     433           0 :   FD_TEST( crds->ci_fresh_15s_dlist );
     434             : 
     435           0 :   crds->hash_treap = hash_treap_join( hash_treap_new( _hash_treap, ele_max ) );
     436           0 :   FD_TEST( crds->hash_treap );
     437           0 :   hash_treap_seed( crds->pool, ele_max, fd_rng_ulong( rng ) );
     438             : 
     439           0 :   crds->lookup_map = lookup_map_join( lookup_map_new( _lookup_map, ele_max, fd_rng_ulong( rng ) ) );
     440           0 :   FD_TEST( crds->lookup_map );
     441             : 
     442           0 :   crds->purged = purged;
     443             : 
     444           0 :   crds->ci_pool = crds_contact_info_pool_join( crds_contact_info_pool_new( _ci_pool, FD_CONTACT_INFO_TABLE_SIZE ) );
     445           0 :   FD_TEST( crds->ci_pool );
     446             : 
     447           0 :   crds->ci_fresh_dlist = crds_contact_info_fresh_list_join( crds_contact_info_fresh_list_new( _ci_dlist ) );
     448           0 :   FD_TEST( crds->ci_fresh_dlist );
     449             : 
     450           0 :   crds->ci_evict_dlist = crds_contact_info_evict_dlist_join( crds_contact_info_evict_dlist_new( _ci_evict_dlist ) );
     451           0 :   FD_TEST( crds->ci_evict_dlist );
     452             : 
     453           0 :   FD_TEST( fd_sha256_join( fd_sha256_new( crds->sha256 ) ) );
     454             : 
     455           0 :   crds->wsample = wsample;
     456           0 :   crds->active_set = active_set;
     457             : 
     458           0 :   FD_TEST( entrypoints_cnt<=16UL );
     459           0 :   for( ulong i=0UL; i<entrypoints_cnt; i++ ) crds->entrypoints[ i ] = entrypoints[ i ];
     460           0 :   crds->entrypoints_cnt = entrypoints_cnt;
     461             : 
     462           0 :   memset( crds->metrics, 0, sizeof(fd_crds_metrics_t) );
     463             : 
     464           0 :   crds->gossip_update   = gossip_update_out;
     465           0 :   crds->has_staked_node = 0;
     466             : 
     467           0 :   FD_COMPILER_MFENCE();
     468           0 :   FD_VOLATILE( crds->magic ) = FD_CRDS_MAGIC;
     469           0 :   FD_COMPILER_MFENCE();
     470             : 
     471           0 :   return (void *)crds;
     472           0 : }
     473             : 
     474             : fd_crds_t *
     475           0 : fd_crds_join( void * shcrds ) {
     476           0 :   if( FD_UNLIKELY( !shcrds ) ) {
     477           0 :     FD_LOG_WARNING(( "NULL shcrds" ));
     478           0 :     return NULL;
     479           0 :   }
     480             : 
     481           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shcrds, fd_crds_align() ) ) ) {
     482           0 :     FD_LOG_WARNING(( "misaligned shcrds" ));
     483           0 :     return NULL;
     484           0 :   }
     485             : 
     486           0 :   fd_crds_t * crds = (fd_crds_t *)shcrds;
     487             : 
     488           0 :   if( FD_UNLIKELY( crds->magic!=FD_CRDS_MAGIC ) ) {
     489           0 :     FD_LOG_WARNING(( "bad magic" ));
     490           0 :     return NULL;
     491           0 :   }
     492             : 
     493           0 :   return crds;
     494           0 : }
     495             : 
     496             : fd_crds_metrics_t const *
     497           0 : fd_crds_metrics( fd_crds_t const * crds ) {
     498           0 :   return crds->metrics;
     499           0 : }
     500             : 
     501             : ulong
     502           0 : fd_crds_len( fd_crds_t const * crds ) {
     503           0 :   return crds_pool_used( crds->pool );
     504           0 : }
     505             : 
     506             : static inline void
     507             : crds_unindex( fd_crds_t *       crds,
     508           0 :               fd_crds_entry_t * entry ) {
     509           0 :   if( FD_LIKELY( entry->stake ) ) staked_expire_dlist_ele_remove( crds->staked_expire_dlist, entry, crds->pool );
     510           0 :   else                            unstaked_expire_dlist_ele_remove( crds->unstaked_expire_dlist, entry, crds->pool );
     511             : 
     512           0 :   evict_treap_ele_remove( crds->evict_treap, entry, crds->pool );
     513           0 :   hash_treap_ele_remove( crds->hash_treap, entry, crds->pool );
     514           0 :   lookup_map_ele_remove( crds->lookup_map, &entry->key, NULL, crds->pool );
     515             : 
     516           0 :   if( FD_UNLIKELY( entry->key.tag==FD_GOSSIP_VALUE_CONTACT_INFO ) ) {
     517           0 :     if( FD_LIKELY( entry->stake ) ) crds->metrics->peer_staked_cnt--;
     518           0 :     else                            crds->metrics->peer_unstaked_cnt--;
     519           0 :     crds->metrics->peer_visible_stake -= entry->stake;
     520             : 
     521           0 :     if( FD_LIKELY( entry->ci->fresh_dlist.in_list ) ) crds_contact_info_fresh_list_ele_remove( crds->ci_fresh_dlist, entry->ci, crds->ci_pool );
     522           0 :     if( FD_LIKELY( entry->ci->fresh_15s_dlist.in_list ) ) {
     523           0 :       ci_fresh_15s_dlist_ele_remove( crds->ci_fresh_15s_dlist, entry->ci, crds->ci_pool );
     524           0 :       crds->activity_update_fn( crds->activity_update_fn_ctx, (fd_pubkey_t const *)entry->key.pubkey, entry->ci->contact_info, FD_GOSSIP_ACTIVITY_CHANGE_TYPE_INACTIVE );
     525           0 :     }
     526           0 :     crds_contact_info_evict_dlist_ele_remove( crds->ci_evict_dlist, entry->ci, crds->ci_pool );
     527           0 :   }
     528             : 
     529           0 :   crds->metrics->count[ entry->key.tag ]--;
     530           0 : }
     531             : 
     532             : static inline void
     533             : crds_index( fd_crds_t *       crds,
     534           0 :             fd_crds_entry_t * entry ) {
     535           0 :   if( FD_LIKELY( entry->stake ) ) staked_expire_dlist_ele_push_tail( crds->staked_expire_dlist, entry, crds->pool );
     536           0 :   else                            unstaked_expire_dlist_ele_push_tail( crds->unstaked_expire_dlist, entry, crds->pool );
     537             : 
     538           0 :   evict_treap_ele_insert( crds->evict_treap, entry, crds->pool );
     539           0 :   hash_treap_ele_insert( crds->hash_treap, entry, crds->pool );
     540           0 :   lookup_map_ele_insert( crds->lookup_map, entry, crds->pool );
     541             : 
     542           0 :   if( FD_UNLIKELY( entry->key.tag==FD_GOSSIP_VALUE_CONTACT_INFO ) ) {
     543           0 :     if( FD_LIKELY( entry->stake ) ) crds->metrics->peer_staked_cnt++;
     544           0 :     else                            crds->metrics->peer_unstaked_cnt++;
     545           0 :     crds->metrics->peer_visible_stake += entry->stake;
     546             : 
     547           0 :     crds_contact_info_evict_dlist_ele_push_tail( crds->ci_evict_dlist, entry->ci, crds->ci_pool );
     548           0 :     crds_contact_info_fresh_list_ele_push_tail( crds->ci_fresh_dlist, entry->ci, crds->ci_pool );
     549           0 :     ci_fresh_15s_dlist_ele_push_tail( crds->ci_fresh_15s_dlist, entry->ci, crds->ci_pool );
     550           0 :     entry->ci->fresh_dlist.in_list = 1;
     551           0 :     entry->ci->fresh_15s_dlist.in_list = 1;
     552           0 :     crds->activity_update_fn( crds->activity_update_fn_ctx, (fd_pubkey_t const *)entry->key.pubkey, entry->ci->contact_info, FD_GOSSIP_ACTIVITY_CHANGE_TYPE_ACTIVE );
     553           0 :   }
     554             : 
     555           0 :   crds->metrics->count[ entry->key.tag ]++;
     556           0 : }
     557             : 
     558             : static inline void
     559             : crds_release( fd_crds_t *         crds,
     560             :               fd_crds_entry_t *   entry,
     561             :               long                now,
     562             :               int                 evicting,
     563           0 :               fd_stem_context_t * stem ) {
     564           0 :   crds_unindex( crds, entry );
     565           0 :   fd_gossip_purged_insert_replaced( crds->purged, entry->value_hash, now );
     566             : 
     567           0 :   if( FD_UNLIKELY( entry->key.tag==FD_GOSSIP_VALUE_CONTACT_INFO ) ) {
     568           0 :     if( FD_UNLIKELY( evicting ) ) crds->metrics->peer_evicted_cnt++;
     569             : 
     570           0 :     fd_gossip_update_message_t * msg = fd_gossip_out_get_chunk( crds->gossip_update );
     571           0 :     msg->tag = FD_GOSSIP_UPDATE_TAG_CONTACT_INFO_REMOVE;
     572           0 :     msg->wallclock = (ulong)FD_NANOSEC_TO_MILLI( now );
     573           0 :     msg->contact_info_remove->idx = crds_contact_info_pool_idx( crds->ci_pool, entry->ci );
     574           0 :     fd_memcpy( msg->origin, entry->key.pubkey, 32UL );
     575           0 :     fd_gossip_tx_publish_chunk( crds->gossip_update, stem, (ulong)msg->tag, FD_GOSSIP_UPDATE_SZ_CONTACT_INFO_REMOVE, now );
     576             : 
     577           0 :     ulong ci_idx = crds_contact_info_pool_idx( crds->ci_pool, entry->ci );
     578           0 :     fd_active_set_remove_peer( crds->active_set, ci_idx );
     579           0 :     fd_gossip_wsample_remove( crds->wsample, ci_idx );
     580             : 
     581           0 :     crds_contact_info_pool_ele_release( crds->ci_pool, entry->ci );
     582           0 :   }
     583             : 
     584           0 :   if( FD_UNLIKELY( evicting ) ) crds->metrics->evicted_cnt++;
     585           0 :   else                          crds->metrics->expired_cnt++;
     586             : 
     587           0 :   crds_pool_ele_release( crds->pool, entry );
     588           0 : }
     589             : 
     590             : static inline fd_crds_entry_t *
     591             : crds_acquire( fd_crds_t *         crds,
     592             :               int                 is_contact_info,
     593             :               long                now,
     594           0 :               fd_stem_context_t * stem ) {
     595           0 :   if( FD_UNLIKELY( is_contact_info ) ) {
     596             :     /* TODO: Should evict lowest stake CI first, or oldest if equally
     597             :        low stake. */
     598           0 :     if( FD_UNLIKELY( !crds_contact_info_pool_free( crds->ci_pool ) ) ) {
     599           0 :       fd_crds_contact_info_entry_t * ci_evict = crds_contact_info_evict_dlist_ele_peek_head( crds->ci_evict_dlist, crds->ci_pool );
     600           0 :       crds_release( crds, ci_evict->crds_entry, now, 1, stem );
     601           0 :     } else if( FD_UNLIKELY( !crds_pool_free( crds->pool ) ) ) {
     602           0 :       evict_treap_fwd_iter_t it = evict_treap_fwd_iter_init( crds->evict_treap, crds->pool );
     603           0 :       FD_TEST( !evict_treap_fwd_iter_done( it ) );
     604           0 :       crds_release( crds, evict_treap_fwd_iter_ele( it, crds->pool ), now, 1, stem );
     605           0 :     }
     606           0 :     fd_crds_contact_info_entry_t * ci = crds_contact_info_pool_ele_acquire( crds->ci_pool );
     607           0 :     fd_crds_entry_t * entry = crds_pool_ele_acquire( crds->pool );
     608           0 :     entry->ci = ci;
     609           0 :     entry->ci->crds_entry = entry;
     610           0 :     return entry;
     611           0 :   } else {
     612           0 :     if( FD_UNLIKELY( !crds_pool_free( crds->pool ) ) ) {
     613           0 :       evict_treap_fwd_iter_t it = evict_treap_fwd_iter_init( crds->evict_treap, crds->pool );
     614           0 :       FD_TEST( !evict_treap_fwd_iter_done( it ) );
     615           0 :       crds_release( crds, evict_treap_fwd_iter_ele( it, crds->pool ), now, 1, stem );
     616           0 :     }
     617           0 :     return crds_pool_ele_acquire( crds->pool );
     618           0 :   }
     619           0 : }
     620             : 
     621             : static inline void
     622             : expire( fd_crds_t *         crds,
     623             :         long                now,
     624             :         fd_stem_context_t * stem,
     625           0 :         int *               charge_busy ){
     626           0 :   static const long SLOT_DURATION_NANOS            = 400L*1000L*1000L;
     627           0 :   static const long STAKED_EXPIRE_DURATION_NANOS   = 432000L*SLOT_DURATION_NANOS;
     628           0 :   static const long UNSTAKED_EXPIRE_DURATION_NANOS = 15L*1000L*1000L*1000L;
     629             : 
     630           0 :   while( !staked_expire_dlist_is_empty( crds->staked_expire_dlist, crds->pool ) ) {
     631           0 :     fd_crds_entry_t * head = staked_expire_dlist_ele_peek_head( crds->staked_expire_dlist, crds->pool );
     632             : 
     633           0 :     if( FD_LIKELY( head->expire.wallclock_nanos>now-STAKED_EXPIRE_DURATION_NANOS ) ) break;
     634           0 :     crds_release( crds, head, now, 0, stem );
     635           0 :     if( charge_busy ) *charge_busy = 1;
     636           0 :   }
     637             : 
     638           0 :   long unstaked_expire_duration_nanos = fd_long_if( crds->has_staked_node,
     639           0 :                                                     UNSTAKED_EXPIRE_DURATION_NANOS,
     640           0 :                                                     STAKED_EXPIRE_DURATION_NANOS );
     641             : 
     642           0 :   while( !unstaked_expire_dlist_is_empty( crds->unstaked_expire_dlist, crds->pool ) ) {
     643           0 :     fd_crds_entry_t * head = unstaked_expire_dlist_ele_peek_head( crds->unstaked_expire_dlist, crds->pool );
     644             : 
     645           0 :     if( FD_LIKELY( head->expire.wallclock_nanos>now-unstaked_expire_duration_nanos ) ) break;
     646           0 :     crds_release( crds, head, now, 0, stem );
     647           0 :     if( charge_busy ) *charge_busy = 1;
     648           0 :   }
     649           0 : }
     650             : 
     651             : static void
     652             : unfresh( fd_crds_t * crds,
     653             :          long        now,
     654           0 :          int *       charge_busy ) {
     655           0 :   while( !crds_contact_info_fresh_list_is_empty( crds->ci_fresh_dlist, crds->ci_pool ) ) {
     656           0 :     fd_crds_contact_info_entry_t * head = crds_contact_info_fresh_list_ele_peek_head( crds->ci_fresh_dlist, crds->ci_pool );
     657             : 
     658           0 :     if( FD_LIKELY( head->received_wallclock_nanos>now-60L*1000L*1000L*1000L ) ) break;
     659           0 :     head = crds_contact_info_fresh_list_ele_pop_head( crds->ci_fresh_dlist, crds->ci_pool );
     660           0 :     FD_TEST( head->fresh_dlist.in_list );
     661           0 :     head->fresh_dlist.in_list = 0;
     662             : 
     663           0 :     fd_gossip_wsample_fresh( crds->wsample, crds_contact_info_pool_idx( crds->ci_pool, head ), 0 );
     664           0 :     if( charge_busy ) *charge_busy = 1;
     665           0 :   }
     666             : 
     667           0 :   while( !ci_fresh_15s_dlist_is_empty( crds->ci_fresh_15s_dlist, crds->ci_pool ) ) {
     668           0 :     fd_crds_contact_info_entry_t * head = ci_fresh_15s_dlist_ele_peek_head( crds->ci_fresh_15s_dlist, crds->ci_pool );
     669             : 
     670           0 :     if( FD_LIKELY( head->received_wallclock_nanos>now-15L*1000L*1000L*1000L ) ) break;
     671             : 
     672           0 :     head = ci_fresh_15s_dlist_ele_pop_head( crds->ci_fresh_15s_dlist, crds->ci_pool );
     673             : 
     674           0 :     FD_TEST( head->fresh_15s_dlist.in_list );
     675           0 :     head->fresh_15s_dlist.in_list = 0U;
     676           0 :     crds->activity_update_fn( crds->activity_update_fn_ctx, (fd_pubkey_t const *)head->crds_entry->key.pubkey, head->contact_info, FD_GOSSIP_ACTIVITY_CHANGE_TYPE_INACTIVE );
     677           0 :     if( charge_busy ) *charge_busy = 1;
     678           0 :   }
     679           0 : }
     680             : 
     681             : void
     682             : fd_crds_advance( fd_crds_t *         crds,
     683             :                  long                now,
     684             :                  fd_stem_context_t * stem,
     685           0 :                  int *               charge_busy ) {
     686           0 :   expire( crds, now, stem, charge_busy );
     687           0 :   unfresh( crds, now, charge_busy );
     688           0 : }
     689             : 
     690             : static inline void
     691             : publish_update_msg( fd_crds_t *               crds,
     692             :                     fd_crds_entry_t *         entry,
     693             :                     fd_gossip_value_t const * entry_view,
     694             :                     long                      now,
     695           0 :                     fd_stem_context_t *       stem ) {
     696           0 :   FD_TEST( stem );
     697           0 :   if( FD_LIKELY( entry->key.tag!=FD_GOSSIP_VALUE_CONTACT_INFO    &&
     698           0 :                  entry->key.tag!=FD_GOSSIP_VALUE_VOTE            &&
     699           0 :                  entry->key.tag!=FD_GOSSIP_VALUE_DUPLICATE_SHRED &&
     700           0 :                  entry->key.tag!=FD_GOSSIP_VALUE_SNAPSHOT_HASHES ) ) {
     701           0 :     return;
     702           0 :   }
     703             : 
     704           0 :   fd_gossip_update_message_t * msg = fd_gossip_out_get_chunk( crds->gossip_update );
     705           0 :   msg->wallclock = entry->wallclock;
     706           0 :   fd_memcpy( msg->origin, entry->key.pubkey, 32UL );
     707             : 
     708           0 :   ulong sz;
     709           0 :   switch( entry->key.tag ) {
     710           0 :     case FD_GOSSIP_VALUE_CONTACT_INFO:
     711           0 :       msg->tag = FD_GOSSIP_UPDATE_TAG_CONTACT_INFO;
     712           0 :       *msg->contact_info->value = *entry->ci->contact_info;
     713           0 :       msg->contact_info->idx = crds_contact_info_pool_idx( crds->ci_pool, entry->ci );
     714           0 :       sz = FD_GOSSIP_UPDATE_SZ_CONTACT_INFO;
     715           0 :       break;
     716           0 :     case FD_GOSSIP_VALUE_VOTE:
     717           0 :       msg->tag = FD_GOSSIP_UPDATE_TAG_VOTE;
     718             :       /* TODO: dynamic sizing */
     719           0 :       sz = FD_GOSSIP_UPDATE_SZ_VOTE;
     720           0 :       fd_crds_key_t lookup_ci;
     721           0 :       lookup_ci.tag = FD_GOSSIP_VALUE_CONTACT_INFO;
     722           0 :       fd_memcpy( &lookup_ci.pubkey, entry->key.pubkey, sizeof(fd_pubkey_t) );
     723           0 :       fd_crds_entry_t * ci = lookup_map_ele_query( crds->lookup_map, &lookup_ci, NULL, crds->pool );
     724             : 
     725           0 :       if( FD_LIKELY( ci && ci->key.tag == FD_GOSSIP_VALUE_CONTACT_INFO ) ) {
     726           0 :         msg->vote->socket->is_ipv6 = ci->ci->contact_info->sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ].is_ipv6;
     727           0 :         if( msg->vote->socket->is_ipv6 ) {
     728           0 :           fd_memcpy( msg->vote->socket->ip6, ci->ci->contact_info->sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ].ip6, 16UL );
     729           0 :         } else {
     730           0 :           msg->vote->socket->ip4 = ci->ci->contact_info->sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ].ip4;
     731           0 :         }
     732           0 :         msg->vote->socket->port = ci->ci->contact_info->sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ].port;
     733           0 :       } else {
     734           0 :         msg->vote->socket->is_ipv6 = 0;
     735           0 :         msg->vote->socket->ip4 = 0;
     736           0 :         msg->vote->socket->port = 0;
     737           0 :       }
     738             : 
     739           0 :       msg->vote->value->index = entry->key.vote_index;
     740           0 :       msg->vote->value->transaction_len = entry_view->vote->transaction_len;
     741           0 :       fd_memcpy( msg->vote->value->transaction, entry_view->vote->transaction, entry_view->vote->transaction_len );
     742           0 :       break;
     743           0 :     case FD_GOSSIP_VALUE_DUPLICATE_SHRED:
     744           0 :       msg->tag = FD_GOSSIP_UPDATE_TAG_DUPLICATE_SHRED;
     745             :       /* TODO: dynamic sizing */
     746           0 :       sz = FD_GOSSIP_UPDATE_SZ_DUPLICATE_SHRED;
     747           0 :       {
     748           0 :         fd_gossip_duplicate_shred_t const * ds     = entry_view->duplicate_shred;
     749           0 :         fd_gossip_duplicate_shred_t *            ds_msg = msg->duplicate_shred;
     750             : 
     751           0 :         ds_msg->index       = ds->index;
     752           0 :         ds_msg->slot        = ds->slot;
     753           0 :         ds_msg->num_chunks  = ds->num_chunks;
     754           0 :         ds_msg->chunk_index = ds->chunk_index;
     755           0 :         ds_msg->chunk_len   = ds->chunk_len;
     756           0 :         fd_memcpy( ds_msg->chunk, ds->chunk, ds->chunk_len );
     757           0 :       }
     758           0 :       break;
     759           0 :     case FD_GOSSIP_VALUE_SNAPSHOT_HASHES:
     760           0 :       msg->tag = FD_GOSSIP_UPDATE_TAG_SNAPSHOT_HASHES;
     761             :       /* TODO: dynamic sizing */
     762           0 :       sz = FD_GOSSIP_UPDATE_SZ_SNAPSHOT_HASHES;
     763           0 :       {
     764           0 :         fd_gossip_snapshot_hashes_t const * sh     = entry_view->snapshot_hashes;
     765           0 :         fd_gossip_snapshot_hashes_t *            sh_msg = msg->snapshot_hashes;
     766             : 
     767           0 :         sh_msg->full_slot = sh->full_slot;
     768           0 :         fd_memcpy( sh_msg->full_hash, sh->full_hash, 32UL );
     769           0 :         sh_msg->incremental_len = sh->incremental_len;
     770           0 :         for( ulong i=0; i<sh->incremental_len; i++ ) {
     771           0 :           sh_msg->incremental[ i ].slot = sh->incremental[ i ].slot;
     772           0 :           fd_memcpy( sh_msg->incremental[ i ].hash, sh->incremental[ i ].hash, 32UL );
     773           0 :         }
     774           0 :       }
     775           0 :       break;
     776           0 :     default:
     777           0 :       FD_LOG_ERR(( "impossible" ));
     778           0 :   }
     779           0 :   fd_gossip_tx_publish_chunk( crds->gossip_update,
     780           0 :                               stem,
     781           0 :                               (ulong)msg->tag,
     782           0 :                               sz,
     783           0 :                               now );
     784           0 : }
     785             : 
     786             : static int
     787             : crds_compare( fd_crds_entry_t const *   incumbent,
     788           0 :               fd_gossip_value_t const * candidate ){
     789           0 :   int compare = 0;
     790           0 :   switch( candidate->tag ) {
     791           0 :     case FD_GOSSIP_VALUE_CONTACT_INFO:
     792           0 :       if( FD_UNLIKELY( candidate->contact_info->outset<incumbent->ci->contact_info->outset ) ) compare = 1;
     793           0 :       else if( FD_UNLIKELY( candidate->contact_info->outset>incumbent->ci->contact_info->outset ) ) compare = -1;
     794           0 :       break;
     795             :     /* NodeInstance has no special override logic in Agave — it uses
     796             :        the default wallclock + hash tiebreaker like all other types. */
     797           0 :     default:
     798           0 :       break;
     799           0 :   }
     800             : 
     801           0 :   if( FD_UNLIKELY( compare ) ) return compare;
     802             : 
     803           0 :   if( FD_UNLIKELY( candidate->wallclock<incumbent->wallclock ) ) return 1;
     804           0 :   else if( FD_UNLIKELY( candidate->wallclock>incumbent->wallclock ) ) return -1;
     805           0 :   else return 0;
     806           0 : }
     807             : 
     808             : static int
     809             : is_entrypoint( fd_crds_t *                crds,
     810           0 :                fd_gossip_socket_t const * socket ) {
     811           0 :   if( FD_UNLIKELY( socket->is_ipv6 ) ) return 0;
     812           0 :   for( ulong i=0UL; i<crds->entrypoints_cnt; i++ ) {
     813           0 :     if( FD_UNLIKELY( socket->ip4==crds->entrypoints[ i ].addr && socket->port==crds->entrypoints[ i ].port ) ) return 1;
     814           0 :   }
     815           0 :   return 0;
     816           0 : }
     817             : 
     818             : long
     819             : fd_crds_insert( fd_crds_t *               crds,
     820             :                 fd_gossip_value_t const * value,
     821             :                 uchar const *             value_bytes,
     822             :                 ulong                     value_bytes_len,
     823             :                 ulong                     origin_stake,
     824             :                 int                       origin_ping_tracked,
     825             :                 int                       is_me,
     826             :                 long                      now ,
     827           0 :                 fd_stem_context_t *       stem ) {
     828           0 :   fd_crds_key_t candidate_key = {
     829           0 :     .tag = (uchar)value->tag,
     830           0 :   };
     831           0 :   switch( candidate_key.tag ) {
     832           0 :     case FD_GOSSIP_VALUE_VOTE: candidate_key.vote_index = value->vote->index; break;
     833           0 :     case FD_GOSSIP_VALUE_EPOCH_SLOTS: candidate_key.epoch_slots_index = value->epoch_slots->index; break;
     834           0 :     case FD_GOSSIP_VALUE_DUPLICATE_SHRED: candidate_key.duplicate_shred_index = value->duplicate_shred->index; break;
     835           0 :     default: break;
     836           0 :   }
     837           0 :   fd_memcpy( candidate_key.pubkey, value->origin, 32UL );
     838             : 
     839           0 :   fd_crds_entry_t * incumbent = lookup_map_ele_query( crds->lookup_map, &candidate_key, NULL, crds->pool );
     840           0 :   int replacing = !!incumbent;
     841             : 
     842           0 :   uchar value_hash[ 32UL ];
     843           0 :   if( FD_UNLIKELY( !replacing ) ) {
     844           0 :     fd_sha256_hash( value_bytes, value_bytes_len, value_hash );
     845             : 
     846           0 :     incumbent = crds_acquire( crds, value->tag==FD_GOSSIP_VALUE_CONTACT_INFO, now, stem );
     847           0 :     incumbent->key = candidate_key;
     848           0 :     if( FD_UNLIKELY( value->tag==FD_GOSSIP_VALUE_CONTACT_INFO ) ) {
     849           0 :       int entrypoint = is_entrypoint( crds, &value->contact_info->sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ] );
     850           0 :       fd_gossip_wsample_add( crds->wsample, crds_contact_info_pool_idx( crds->ci_pool, incumbent->ci ), origin_stake, origin_ping_tracked, entrypoint, is_me );
     851           0 :     }
     852           0 :   } else {
     853             :     /* Fast duplicate check by signature before computing expensive
     854             :        sha256 hash. */
     855           0 :     if( FD_UNLIKELY( fd_ulong_load_8( incumbent->value_bytes )==fd_ulong_load_8( value->signature ) ) ) return (long)(++incumbent->num_duplicates);
     856             : 
     857           0 :     fd_sha256_hash( value_bytes, value_bytes_len, value_hash );
     858           0 :     switch( crds_compare( incumbent, value ) ) {
     859           0 :       case -1: break; /* upserting */
     860           0 :       case 0: {
     861           0 :         int result = memcmp( value_hash, incumbent->value_hash, 32UL );
     862           0 :         if( FD_UNLIKELY( !result ) ) return (long)(++incumbent->num_duplicates);
     863           0 :         else if( FD_UNLIKELY( result<0 ) ) {
     864           0 :           fd_gossip_purged_insert_failed_insert( crds->purged, value_hash, now );
     865           0 :           return -1L; /* stale */
     866           0 :         }
     867           0 :         else break; /* upserting */
     868           0 :       }
     869           0 :       case 1: {
     870           0 :         fd_gossip_purged_insert_failed_insert( crds->purged, value_hash, now );
     871           0 :         return -1L; /* stale */
     872           0 :       }
     873           0 :     }
     874             : 
     875           0 :     fd_gossip_purged_insert_replaced( crds->purged, incumbent->value_hash, now );
     876           0 :     crds_unindex( crds, incumbent );
     877             : 
     878           0 :     if( FD_UNLIKELY( value->tag==FD_GOSSIP_VALUE_CONTACT_INFO ) ) {
     879           0 :       fd_gossip_wsample_fresh( crds->wsample, crds_contact_info_pool_idx( crds->ci_pool, incumbent->ci ), 1 );
     880           0 :       fd_gossip_wsample_stake( crds->wsample, crds_contact_info_pool_idx( crds->ci_pool, incumbent->ci ), origin_stake );
     881           0 :       fd_gossip_wsample_ping_tracked( crds->wsample, crds_contact_info_pool_idx( crds->ci_pool, incumbent->ci ), origin_ping_tracked );
     882           0 :       int entrypoint = is_entrypoint( crds, &value->contact_info->sockets[ FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ] );
     883           0 :       fd_gossip_wsample_is_entrypoint( crds->wsample, crds_contact_info_pool_idx( crds->ci_pool, incumbent->ci ), entrypoint );
     884           0 :       fd_gossip_wsample_is_me( crds->wsample, crds_contact_info_pool_idx( crds->ci_pool, incumbent->ci ), is_me );
     885           0 :     }
     886           0 :   }
     887             : 
     888           0 :   incumbent->wallclock              = value->wallclock;
     889           0 :   incumbent->stake                  = origin_stake;
     890           0 :   incumbent->num_duplicates         = 0UL;
     891           0 :   incumbent->expire.wallclock_nanos = now;
     892           0 :   incumbent->value_sz               = (ushort)value_bytes_len;
     893           0 :   fd_memcpy( incumbent->value_bytes, value_bytes, value_bytes_len );
     894           0 :   fd_memcpy( incumbent->value_hash, value_hash, 32UL );
     895           0 :   incumbent->hash.hash_prefix = fd_ulong_load_8( incumbent->value_hash );
     896             : 
     897           0 :   if( FD_UNLIKELY( value->tag==FD_GOSSIP_VALUE_NODE_INSTANCE ) ) {
     898           0 :     incumbent->node_instance_token = value->node_instance->token;
     899           0 :   } else if( FD_UNLIKELY( value->tag==FD_GOSSIP_VALUE_CONTACT_INFO ) ) {
     900           0 :     *incumbent->ci->contact_info            = *value->contact_info;
     901           0 :     incumbent->ci->received_wallclock_nanos = now;
     902           0 :   }
     903             : 
     904           0 :   crds_index( crds, incumbent );
     905             : 
     906           0 :   crds->has_staked_node |= incumbent->stake ? 1 : 0;
     907             : 
     908           0 :   publish_update_msg( crds, incumbent, value, now, stem );
     909             : 
     910           0 :   return 0L;
     911           0 : }
     912             : 
     913             : void
     914             : fd_crds_entry_value( fd_crds_entry_t const * entry,
     915             :                      uchar const **          value_bytes,
     916           0 :                      ulong *                 value_sz ) {
     917           0 :   *value_bytes = entry->value_bytes;
     918           0 :   *value_sz    = entry->value_sz;
     919           0 : }
     920             : 
     921             : ulong
     922           0 : fd_crds_entry_wallclock( fd_crds_entry_t const * entry ) {
     923           0 :   return entry->wallclock;
     924           0 : }
     925             : 
     926             : uchar const *
     927           0 : fd_crds_entry_hash( fd_crds_entry_t const * entry ) {
     928           0 :   return entry->value_hash;
     929           0 : }
     930             : 
     931             : ulong
     932           0 : fd_crds_peer_count( fd_crds_t const * crds ){
     933           0 :   return crds_contact_info_pool_used( crds->ci_pool );
     934           0 : }
     935             : 
     936             : fd_gossip_contact_info_t const *
     937             : fd_crds_ci( fd_crds_t const * crds,
     938           0 :             ulong             ci_idx ) {
     939           0 :   fd_crds_contact_info_entry_t const * ci = crds_contact_info_pool_ele_const( crds->ci_pool, ci_idx );
     940           0 :   FD_TEST( ci );
     941           0 :   return ci->contact_info;
     942           0 : }
     943             : 
     944             : uchar const *
     945             : fd_crds_ci_pubkey( fd_crds_t const * crds,
     946           0 :                    ulong             ci_idx ) {
     947           0 :   fd_crds_contact_info_entry_t const * ci = crds_contact_info_pool_ele_const( crds->ci_pool, ci_idx );
     948           0 :   FD_TEST( ci );
     949           0 :   return ci->crds_entry->key.pubkey;
     950           0 : }
     951             : 
     952             : ulong
     953             : fd_crds_ci_idx( fd_crds_t const * crds,
     954           0 :                 uchar const *     pubkey ) {
     955           0 :   fd_crds_key_t lookup_ci = {
     956           0 :     .tag = FD_GOSSIP_VALUE_CONTACT_INFO,
     957           0 :   };
     958           0 :   fd_memcpy( lookup_ci.pubkey, pubkey, 32UL );
     959             : 
     960           0 :   fd_crds_entry_t const * ci_entry = lookup_map_ele_query( crds->lookup_map, &lookup_ci, NULL, crds->pool );
     961           0 :   if( FD_UNLIKELY( !ci_entry ) ) return ULONG_MAX;
     962           0 :   FD_TEST( ci_entry->key.tag==FD_GOSSIP_VALUE_CONTACT_INFO );
     963           0 :   return crds_contact_info_pool_idx( crds->ci_pool, ci_entry->ci );
     964           0 : }
     965             : 
     966             : struct fd_crds_mask_iter_private {
     967             :   ulong idx;
     968             :   ulong end_hash;
     969             : };
     970             : 
     971             : fd_crds_mask_iter_t *
     972             : fd_crds_mask_iter_init( fd_crds_t const * crds,
     973             :                         ulong             mask,
     974             :                         uint              mask_bits,
     975           0 :                         uchar             iter_mem[ static 16UL ] ) {
     976           0 :   ulong start_hash, end_hash;
     977           0 :   fd_gossip_purged_generate_masks( mask, mask_bits, &start_hash, &end_hash );
     978             : 
     979           0 :   fd_crds_mask_iter_t * it = (fd_crds_mask_iter_t *)iter_mem;
     980           0 :   it->end_hash             = end_hash;
     981           0 :   it->idx                  = hash_treap_idx_ge( crds->hash_treap, start_hash, crds->pool );
     982           0 :   return it;
     983           0 : }
     984             : 
     985             : fd_crds_mask_iter_t *
     986           0 : fd_crds_mask_iter_next( fd_crds_mask_iter_t * it, fd_crds_t const * crds ) {
     987           0 :   fd_crds_entry_t const * val = hash_treap_ele_fast_const( it->idx, crds->pool );
     988           0 :   it->idx                     = val->hash.next;
     989           0 :   return it;
     990           0 : }
     991             : 
     992             : int
     993           0 : fd_crds_mask_iter_done( fd_crds_mask_iter_t * it, fd_crds_t const * crds ) {
     994           0 :   if( FD_UNLIKELY( hash_treap_idx_is_null( it->idx ) ) ) return 1;
     995           0 :   fd_crds_entry_t const * val = hash_treap_ele_fast_const( it->idx, crds->pool );
     996           0 :   return it->end_hash < val->hash.hash_prefix;
     997           0 : }
     998             : 
     999             : fd_crds_entry_t const *
    1000           0 : fd_crds_mask_iter_entry( fd_crds_mask_iter_t * it, fd_crds_t const * crds ){
    1001           0 :   return hash_treap_ele_fast_const( it->idx, crds->pool );
    1002           0 : }

Generated by: LCOV version 1.14