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