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-06-02 08:47:55 Functions: 0 30 0.0 %

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

Generated by: LCOV version 1.14