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 : }
|