LCOV - code coverage report
Current view: top level - flamenco/gossip - fd_crds.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 572 0.0 %
Date: 2026-11-11 12:40:43 Functions: 0 30 0.0 %

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

Generated by: LCOV version 1.14