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

Generated by: LCOV version 1.14