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